private static List<? extends TypeMirror> computeParametrizedType( Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset, TypeMirror[] typeParameterBound, int[] numTypeParameters) { ParameterizedTypeTree ptt = (ParameterizedTypeTree) parent.getLeaf(); if (ptt.getType() == error) { Tree gpt = parent.getParentPath().getLeaf(); if (TreeUtilities.CLASS_TREE_KINDS.contains(gpt.getKind()) && ((ClassTree) gpt).getExtendsClause() == ptt) { types.add(ElementKind.CLASS); } else if (TreeUtilities.CLASS_TREE_KINDS.contains(gpt.getKind()) && ((ClassTree) gpt).getImplementsClause().contains(ptt)) { types.add(ElementKind.INTERFACE); } else { types.add(ElementKind.CLASS); types.add(ElementKind.INTERFACE); } if (numTypeParameters != null) { numTypeParameters[0] = ptt.getTypeArguments().size(); } return null; } TypeMirror resolved = info.getTrees().getTypeMirror(parent); DeclaredType resolvedDT = null; if (resolved != null && resolved.getKind() == TypeKind.DECLARED) { resolvedDT = (DeclaredType) resolved; } int index = 0; for (Tree t : ptt.getTypeArguments()) { if (t == error) { if (resolvedDT != null && typeParameterBound != null) { List<? extends TypeMirror> typeArguments = ((DeclaredType) resolvedDT.asElement().asType()).getTypeArguments(); if (typeArguments.size() > index) { typeParameterBound[0] = ((TypeVariable) typeArguments.get(index)).getUpperBound(); } } types.add(ElementKind.CLASS); // XXX: class/interface/enum/annotation? return null; } index++; } return null; }
@Override public AnnotatedTypeMirror getAnnotatedType(Tree tree) { if (tree.getKind() == Tree.Kind.POSTFIX_DECREMENT || tree.getKind() == Tree.Kind.POSTFIX_INCREMENT) { return getPostFixAnno((UnaryTree) tree, super.getAnnotatedType(tree)); } else { return super.getAnnotatedType(tree); } }
public StringConcatenateNode(Tree tree, Node left, Node right) { super(InternalUtils.typeOf(tree)); assert tree.getKind() == Kind.PLUS; this.tree = tree; this.left = left; this.right = right; }
@Override public AnnotatedDeclaredType getSelfType(Tree tree) { AnnotatedDeclaredType selfType = super.getSelfType(tree); TreePath path = getPath(tree); Tree topLevelMember = findTopLevelClassMemberForTree(path); if (topLevelMember != null) { if (topLevelMember.getKind() != Kind.METHOD || TreeUtils.isConstructor((MethodTree) topLevelMember)) { setSelfTypeInInitializationCode(tree, selfType, path); } } return selfType; }
@Override public boolean isValidUse(AnnotatedPrimitiveType type, Tree tree) { if (tree.getKind() != Tree.Kind.TYPE_CAST && !type.hasAnnotation(NONNULL)) { // TODO: casts are sometimes inferred as @Nullable. // Find a way to correctly handle that case. return false; } return super.isValidUse(type, tree); }
private static List<? extends TypeMirror> computeNewClass( Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { NewClassTree nct = (NewClassTree) parent.getLeaf(); boolean errorInRealArguments = false; for (Tree param : nct.getArguments()) { errorInRealArguments |= param == error; } if (errorInRealArguments) { TypeMirror[] proposedType = new TypeMirror[1]; int[] proposedIndex = new int[1]; ExecutableElement ee = org.netbeans.modules.editor.java.Utilities.fuzzyResolveMethodInvocation( info, parent, proposedType, proposedIndex); if (ee == null) { // cannot be resolved return null; } types.add(ElementKind.PARAMETER); types.add(ElementKind.LOCAL_VARIABLE); types.add(ElementKind.FIELD); return Collections.singletonList(proposedType[0]); } Tree id = nct.getIdentifier(); if (id.getKind() == Kind.PARAMETERIZED_TYPE) { id = ((ParameterizedTypeTree) id).getType(); } if (id == error) { return resolveType( EnumSet.noneOf(ElementKind.class), info, parent.getParentPath(), nct, offset, null, null); } return null; }
/** Are all fields committed-only? */ protected boolean areAllFieldsCommittedOnly(ClassTree classTree) { if (!useFbc) { // In the rawness type system, no fields can store not fully // initialized objects. return true; } for (Tree member : classTree.getMembers()) { if (!member.getKind().equals(Tree.Kind.VARIABLE)) { continue; } VariableTree var = (VariableTree) member; VariableElement varElt = TreeUtils.elementFromDeclaration(var); // var is not committed-only if (getDeclAnnotation(varElt, NotOnlyInitialized.class) != null) { // var is not static -- need a check of initializer blocks, // not of constructor which is where this is used if (!varElt.getModifiers().contains(Modifier.STATIC)) { return false; } } } return true; }
@Override public boolean isValidUse( AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { // At most a single qualifier on a type, ignoring a possible PolyAll // annotation. boolean foundInit = false; boolean foundNonNull = false; Set<Class<? extends Annotation>> initQuals = atypeFactory.getInitializationAnnotations(); Set<Class<? extends Annotation>> nonNullQuals = atypeFactory.getNullnessAnnotations(); for (AnnotationMirror anno : useType.getAnnotations()) { if (QualifierPolymorphism.isPolyAll(anno)) { // ok. } else if (containsSameIgnoringValues(initQuals, anno)) { if (foundInit) { return false; } foundInit = true; } else if (containsSameIgnoringValues(nonNullQuals, anno)) { if (foundNonNull) { return false; } foundNonNull = true; } } if (tree.getKind() == Tree.Kind.VARIABLE) { Element vs = InternalUtils.symbol(tree); switch (vs.getKind()) { case EXCEPTION_PARAMETER: if (useType.hasAnnotation(NULLABLE)) { // Exception parameters cannot use Nullable // annotations. They default to NonNull. return false; } break; default: // nothing to do break; } } // The super implementation checks that useType is a subtype // of declarationType. However, declarationType by default // is NonNull, which would then forbid Nullable uses. // Therefore, don't perform this check. return true; }
@Override protected void commonAssignmentCheck( Tree varTree, ExpressionTree valueExp, /*@CompilerMessageKey*/ String errorKey) { // allow MonotonicNonNull to be initialized to null at declaration if (varTree.getKind() == Tree.Kind.VARIABLE) { Element elem = TreeUtils.elementFromDeclaration((VariableTree) varTree); if (atypeFactory.fromElement(elem).hasEffectiveAnnotation(MONOTONIC_NONNULL) && !checker.getLintOption( AbstractNullnessChecker.LINT_NOINITFORMONOTONICNONNULL, AbstractNullnessChecker.LINT_DEFAULT_NOINITFORMONOTONICNONNULL)) { return; } } super.commonAssignmentCheck(varTree, valueExp, errorKey); }
@Override public boolean isAssignable(AnnotatedTypeMirror varType, Tree varTree) { if (varTree.getKind() == Tree.Kind.VARIABLE || varType.hasAnnotation(ASSIGNABLE)) return true; Element varElement = InternalUtils.symbol(varTree); if (varElement == null || !varElement.getKind().isField() || ElementUtils.isStatic(varElement)) return true; ExpressionTree expTree = (ExpressionTree) varTree; AnnotatedTypeMirror receiver = factory.getReceiver(expTree); boolean isAssignable = receiver.hasAnnotation(MUTABLE) || (receiver.hasAnnotation(ASSIGNS_FIELDS) && TreeUtils.isSelfAccess(expTree)); return isAssignable; }
@Override public AnnotatedTypeMirror getAnnotatedType(Tree tree) { AnnotatedTypeMirror type = super.getAnnotatedType(tree); if (tree instanceof ExpressionTree) tree = TreeUtils.skipParens((ExpressionTree) tree); Element elt = InternalUtils.symbol(tree); if (!checker.isChecking() /*&& !checker.isDefaultAnyType(type)*/ && type.isAnnotated() && !type.getKind().isPrimitive() && type.getKind() != TypeKind.NULL && (elt == null || !ElementUtils.isStatic(elt))) { // We don't want annotations on the following trees Kind kind = tree.getKind(); if (kind == Kind.METHOD_INVOCATION || kind == Kind.ARRAY_ACCESS || kind == Kind.MEMBER_SELECT || kind == Kind.CONDITIONAL_EXPRESSION) { type = type.getCopy(false); } } return type; }
static Tree skipParens(Tree tree) { return tree.accept(SKIP_PARENS, null); }
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); }
/** * Pattern matches to prevent false positives of the form {@code (a == b || a.compareTo(b) == 0)}. * Returns true iff the given node fits this pattern. * * @param node * @return true iff the node fits the pattern (a == b || a.compareTo(b) == 0) */ private boolean suppressEarlyCompareTo(final BinaryTree node) { // Only handle == binary trees if (node.getKind() != Tree.Kind.EQUAL_TO) return false; Tree left = node.getLeftOperand(); Tree right = node.getRightOperand(); // Only valid if we're comparing identifiers. if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) { return false; } final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left); final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right); // looking for ((a == b || a.compareTo(b) == 0) Heuristics.Matcher matcher = new Heuristics.Matcher() { @Override public Boolean visitBinary(BinaryTree tree, Void p) { if (tree.getKind() == Tree.Kind.EQUAL_TO) { // a.compareTo(b) == 0 ExpressionTree leftTree = tree.getLeftOperand(); // looking for a.compareTo(b) or b.compareTo(a) ExpressionTree rightTree = tree.getRightOperand(); // looking for 0 if (rightTree.getKind() != Tree.Kind.INT_LITERAL) { return false; } LiteralTree rightLiteral = (LiteralTree) rightTree; if (!rightLiteral.getValue().equals(0)) { return false; } return visit(leftTree, p); } else { // a == b || a.compareTo(b) == 0 ExpressionTree leftTree = tree.getLeftOperand(); // looking for a==b ExpressionTree rightTree = tree.getRightOperand(); // looking for a.compareTo(b) == 0 or b.compareTo(a) == 0 if (leftTree != node) { return false; } if (rightTree.getKind() != Tree.Kind.EQUAL_TO) { return false; } return visit(rightTree, p); } } @Override public Boolean visitMethodInvocation(MethodInvocationTree tree, Void p) { if (!isInvocationOfCompareTo(tree)) { return false; } List<? extends ExpressionTree> args = tree.getArguments(); if (args.size() != 1) { return false; } ExpressionTree arg = args.get(0); if (arg.getKind() != Tree.Kind.IDENTIFIER) { return false; } Element argElt = TreeUtils.elementFromUse(arg); ExpressionTree exp = tree.getMethodSelect(); if (exp.getKind() != Tree.Kind.MEMBER_SELECT) { return false; } MemberSelectTree member = (MemberSelectTree) exp; if (member.getExpression().getKind() != Tree.Kind.IDENTIFIER) { return false; } Element refElt = TreeUtils.elementFromUse(member.getExpression()); if (!((refElt.equals(lhs) && argElt.equals(rhs)) || ((refElt.equals(rhs) && argElt.equals(lhs))))) { return false; } return true; } }; boolean okay = Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.CONDITIONAL_OR, matcher)) .match(getCurrentPath()); return okay; }
// TODO: handle != comparisons too! private boolean suppressInsideComparison(final BinaryTree node) { // Only handle == binary trees if (node.getKind() != Tree.Kind.EQUAL_TO) return false; Tree left = node.getLeftOperand(); Tree right = node.getRightOperand(); // Only valid if we're comparing identifiers. if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) return false; // If we're not directly in an if statement in a method (ignoring // parens and blocks), terminate. // TODO: only if it's the first statement in the block if (!Heuristics.matchParents(getCurrentPath(), Tree.Kind.IF, Tree.Kind.METHOD)) return false; ExecutableElement enclosing = TreeUtils.elementFromDeclaration(visitorState.getMethodTree()); assert enclosing != null; final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left); final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right); // Matcher to check for if statement that returns zero Heuristics.Matcher matcher = new Heuristics.Matcher() { @Override public Boolean visitIf(IfTree tree, Void p) { return visit(tree.getThenStatement(), p); } @Override public Boolean visitBlock(BlockTree tree, Void p) { if (tree.getStatements().size() > 0) return visit(tree.getStatements().get(0), p); return false; } @Override public Boolean visitReturn(ReturnTree tree, Void p) { ExpressionTree expr = tree.getExpression(); return (expr != null && expr.getKind() == Tree.Kind.INT_LITERAL && ((LiteralTree) expr).getValue().equals(0)); } }; // Determine whether or not the "then" statement of the if has a single // "return 0" statement (for the Comparator.compare heuristic). if (overrides(enclosing, Comparator.class, "compare")) { final boolean returnsZero = Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcher)) .match(getCurrentPath()); if (!returnsZero) return false; assert enclosing.getParameters().size() == 2; Element p1 = enclosing.getParameters().get(0); Element p2 = enclosing.getParameters().get(1); return (p1.equals(lhs) && p2.equals(rhs)) || (p2.equals(lhs) && p1.equals(rhs)); } else if (overrides(enclosing, Object.class, "equals")) { assert enclosing.getParameters().size() == 1; Element param = enclosing.getParameters().get(0); Element thisElt = getThis(trees.getScope(getCurrentPath())); assert thisElt != null; return (thisElt.equals(lhs) && param.equals(rhs)) || (param.equals(lhs) && thisElt.equals(rhs)); } else if (overrides(enclosing, Comparable.class, "compareTo")) { final boolean returnsZero = Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcher)) .match(getCurrentPath()); if (!returnsZero) { return false; } assert enclosing.getParameters().size() == 1; Element param = enclosing.getParameters().get(0); Element thisElt = getThis(trees.getScope(getCurrentPath())); assert thisElt != null; return (thisElt.equals(lhs) && param.equals(rhs)) || (param.equals(lhs) && thisElt.equals(rhs)); } return false; }
@Override public boolean matches(Tree tree, VisitorState state) { FirstMatchingScanner scanner = new FirstMatchingScanner(state); Boolean matchFound = tree.accept(scanner, false); return matchFound != null && matchFound; }