use of com.sun.source.tree.NewArrayTree in project checker-framework by typetools.
the class UpperBoundVisitor method relaxedCommonAssignment.
/**
* Returns whether the assignment is legal based on the relaxed assignment rules.
*
* <p>The relaxed assignment rules are the following: Assuming the varType (left-hand side) is
* less than the length of some array given some offset
*
* <p>1. If both the offset and the value expression (rhs) are ints known at compile time, and if
* the min length of the array is greater than offset + value, then the assignment is legal. (This
* method returns true.)
*
* <p>2. If the value expression (rhs) is less than the length of an array that is the same length
* as the array in the varType, and if the offsets are equal, then the assignment is legal. (This
* method returns true.)
*
* <p>3. Otherwise the assignment is only legal if the usual assignment rules are true, so this
* method returns false.
*
* <p>If the varType is less than the length of multiple arrays, then this method only returns
* true if the relaxed rules above apply for each array.
*
* <p>If the varType is an array type and the value expression is an array initializer, then the
* above rules are applied for expression in the initializer where the varType is the component
* type of the array.
*
* @param varType the type of the left-hand side (the variable in the assignment)
* @param valueExp the right-hand side (the expression in the assignment)
* @return true if the assignment is legal based on special Upper Bound rules
*/
private boolean relaxedCommonAssignment(AnnotatedTypeMirror varType, ExpressionTree valueExp) {
if (valueExp.getKind() == Tree.Kind.NEW_ARRAY && varType.getKind() == TypeKind.ARRAY) {
List<? extends ExpressionTree> expressions = ((NewArrayTree) valueExp).getInitializers();
if (expressions == null || expressions.isEmpty()) {
return false;
}
// The qualifier we need for an array is in the component type, not varType.
AnnotatedTypeMirror componentType = ((AnnotatedArrayType) varType).getComponentType();
UBQualifier qualifier = UBQualifier.createUBQualifier(componentType, atypeFactory.UNKNOWN, (UpperBoundChecker) checker);
if (!qualifier.isLessThanLengthQualifier()) {
return false;
}
for (ExpressionTree expressionTree : expressions) {
if (!relaxedCommonAssignmentCheck((LessThanLengthOf) qualifier, expressionTree)) {
return false;
}
}
return true;
}
UBQualifier qualifier = UBQualifier.createUBQualifier(varType, atypeFactory.UNKNOWN, (UpperBoundChecker) checker);
return qualifier.isLessThanLengthQualifier() && relaxedCommonAssignmentCheck((LessThanLengthOf) qualifier, valueExp);
}
use of com.sun.source.tree.NewArrayTree in project checker-framework by typetools.
the class PropagationTreeAnnotator method visitNewArray.
@Override
public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) {
assert type.getKind() == TypeKind.ARRAY : "PropagationTreeAnnotator.visitNewArray: should be an array type";
AnnotatedTypeMirror componentType = ((AnnotatedArrayType) type).getComponentType();
// prev is the lub of the initializers if they exist, otherwise the current component type.
Set<? extends AnnotationMirror> prev = null;
if (tree.getInitializers() != null && !tree.getInitializers().isEmpty()) {
// the array.
for (ExpressionTree init : tree.getInitializers()) {
AnnotatedTypeMirror initType = atypeFactory.getAnnotatedType(init);
// initType might be a typeVariable, so use effectiveAnnotations.
Set<AnnotationMirror> annos = initType.getEffectiveAnnotations();
prev = (prev == null) ? annos : qualHierarchy.leastUpperBounds(prev, annos);
}
} else {
prev = componentType.getAnnotations();
}
assert prev != null : "PropagationTreeAnnotator.visitNewArray: violated assumption about qualifiers";
TreePath path = atypeFactory.getPath(tree);
AnnotatedTypeMirror contextType = null;
if (path != null && path.getParentPath() != null) {
Tree parentTree = path.getParentPath().getLeaf();
if (parentTree.getKind() == Kind.ASSIGNMENT) {
Tree var = ((AssignmentTree) parentTree).getVariable();
contextType = atypeFactory.getAnnotatedType(var);
} else if (parentTree.getKind() == Kind.VARIABLE) {
contextType = atypeFactory.getAnnotatedType(parentTree);
} else if (parentTree instanceof CompoundAssignmentTree) {
Tree var = ((CompoundAssignmentTree) parentTree).getVariable();
contextType = atypeFactory.getAnnotatedType(var);
} else if (parentTree.getKind() == Kind.RETURN) {
Tree methodTree = TreePathUtil.enclosingMethodOrLambda(path.getParentPath());
if (methodTree.getKind() == Kind.METHOD) {
AnnotatedExecutableType methodType = atypeFactory.getAnnotatedType((MethodTree) methodTree);
contextType = methodType.getReturnType();
}
} else if (parentTree.getKind() == Kind.METHOD_INVOCATION && useAssignmentContext) {
MethodInvocationTree methodInvocationTree = (MethodInvocationTree) parentTree;
useAssignmentContext = false;
AnnotatedExecutableType m;
try {
if (atypeFactory.shouldCache && methodInvocationToType.containsKey(methodInvocationTree)) {
m = methodInvocationToType.get(methodInvocationTree);
} else {
m = atypeFactory.methodFromUse(methodInvocationTree).executableType;
if (atypeFactory.shouldCache) {
methodInvocationToType.put(methodInvocationTree, m);
}
}
} finally {
useAssignmentContext = true;
}
for (int i = 0; i < m.getParameterTypes().size(); i++) {
// Tree must be exactly the same.
@SuppressWarnings("interning") boolean foundArgument = methodInvocationTree.getArguments().get(i) == tree;
if (foundArgument) {
contextType = m.getParameterTypes().get(i);
break;
}
}
}
}
Set<? extends AnnotationMirror> post;
if (contextType instanceof AnnotatedArrayType) {
AnnotatedTypeMirror contextComponentType = ((AnnotatedArrayType) contextType).getComponentType();
// Only compare the qualifiers that existed in the array type.
// Defaulting wasn't performed yet, so prev might have fewer qualifiers than
// contextComponentType, which would cause a failure.
// TODO: better solution?
boolean prevIsSubtype = true;
for (AnnotationMirror am : prev) {
if (contextComponentType.isAnnotatedInHierarchy(am) && !this.qualHierarchy.isSubtype(am, contextComponentType.getAnnotationInHierarchy(am))) {
prevIsSubtype = false;
}
}
// It fails for array initializer expressions. Those should be handled nicer.
if (contextComponentType.getKind() == componentType.getKind() && (prev.isEmpty() || (!contextComponentType.getAnnotations().isEmpty() && prevIsSubtype))) {
post = contextComponentType.getAnnotations();
} else {
// The type of the array initializers is incompatible with the context type!
// Somebody else will complain.
post = prev;
}
} else {
// No context is available - simply use what we have.
post = prev;
}
// TODO (issue #599): This only works at the top level. It should work at all levels of
// the array.
addAnnoOrBound(componentType, post);
return null;
}
use of com.sun.source.tree.NewArrayTree in project checker-framework by typetools.
the class JavaExpression method fromTree.
/**
* Converts a javac {@link ExpressionTree} to a CF JavaExpression. The result might contain {@link
* Unknown}.
*
* <p>We ignore operations such as widening and narrowing when computing the JavaExpression.
*
* @param tree a javac tree
* @return a JavaExpression for the given javac tree
*/
public static JavaExpression fromTree(ExpressionTree tree) {
JavaExpression result;
switch(tree.getKind()) {
case ARRAY_ACCESS:
ArrayAccessTree a = (ArrayAccessTree) tree;
JavaExpression arrayAccessExpression = fromTree(a.getExpression());
JavaExpression index = fromTree(a.getIndex());
result = new ArrayAccess(TreeUtils.typeOf(a), arrayAccessExpression, index);
break;
case BOOLEAN_LITERAL:
case CHAR_LITERAL:
case DOUBLE_LITERAL:
case FLOAT_LITERAL:
case INT_LITERAL:
case LONG_LITERAL:
case NULL_LITERAL:
case STRING_LITERAL:
LiteralTree vn = (LiteralTree) tree;
result = new ValueLiteral(TreeUtils.typeOf(tree), vn.getValue());
break;
case NEW_ARRAY:
NewArrayTree newArrayTree = (NewArrayTree) tree;
List<@Nullable JavaExpression> dimensions;
if (newArrayTree.getDimensions() == null) {
dimensions = Collections.emptyList();
} else {
dimensions = new ArrayList<>(newArrayTree.getDimensions().size());
for (ExpressionTree dimension : newArrayTree.getDimensions()) {
dimensions.add(fromTree(dimension));
}
}
List<JavaExpression> initializers;
if (newArrayTree.getInitializers() == null) {
initializers = Collections.emptyList();
} else {
initializers = new ArrayList<>(newArrayTree.getInitializers().size());
for (ExpressionTree initializer : newArrayTree.getInitializers()) {
initializers.add(fromTree(initializer));
}
}
result = new ArrayCreation(TreeUtils.typeOf(tree), dimensions, initializers);
break;
case METHOD_INVOCATION:
MethodInvocationTree mn = (MethodInvocationTree) tree;
assert TreeUtils.isUseOfElement(mn) : "@AssumeAssertion(nullness): tree kind";
ExecutableElement invokedMethod = TreeUtils.elementFromUse(mn);
// Note that the method might be nondeterministic.
List<JavaExpression> parameters = CollectionsPlume.mapList(JavaExpression::fromTree, mn.getArguments());
JavaExpression methodReceiver;
if (ElementUtils.isStatic(invokedMethod)) {
@SuppressWarnings(// enclosingTypeElement(ExecutableElement): @NonNull
"nullness:assignment") @NonNull TypeElement methodType = ElementUtils.enclosingTypeElement(invokedMethod);
methodReceiver = new ClassName(methodType.asType());
} else {
methodReceiver = getReceiver(mn);
}
TypeMirror resultType = TreeUtils.typeOf(mn);
result = new MethodCall(resultType, invokedMethod, methodReceiver, parameters);
break;
case MEMBER_SELECT:
result = fromMemberSelect((MemberSelectTree) tree);
break;
case IDENTIFIER:
IdentifierTree identifierTree = (IdentifierTree) tree;
TypeMirror typeOfId = TreeUtils.typeOf(identifierTree);
Name identifierName = identifierTree.getName();
if (identifierName.contentEquals("this") || identifierName.contentEquals("super")) {
result = new ThisReference(typeOfId);
break;
}
assert TreeUtils.isUseOfElement(identifierTree) : "@AssumeAssertion(nullness): tree kind";
Element ele = TreeUtils.elementFromUse(identifierTree);
if (ElementUtils.isTypeElement(ele)) {
result = new ClassName(ele.asType());
break;
}
result = fromVariableElement(typeOfId, ele);
break;
case UNARY_PLUS:
return fromTree(((UnaryTree) tree).getExpression());
case BITWISE_COMPLEMENT:
case LOGICAL_COMPLEMENT:
case POSTFIX_DECREMENT:
case POSTFIX_INCREMENT:
case PREFIX_DECREMENT:
case PREFIX_INCREMENT:
case UNARY_MINUS:
JavaExpression operand = fromTree(((UnaryTree) tree).getExpression());
return new UnaryOperation(TreeUtils.typeOf(tree), tree.getKind(), operand);
case CONDITIONAL_AND:
case CONDITIONAL_OR:
case DIVIDE:
case EQUAL_TO:
case GREATER_THAN:
case GREATER_THAN_EQUAL:
case LEFT_SHIFT:
case LESS_THAN:
case LESS_THAN_EQUAL:
case MINUS:
case MULTIPLY:
case NOT_EQUAL_TO:
case OR:
case PLUS:
case REMAINDER:
case RIGHT_SHIFT:
case UNSIGNED_RIGHT_SHIFT:
case XOR:
BinaryTree binaryTree = (BinaryTree) tree;
JavaExpression left = fromTree(binaryTree.getLeftOperand());
JavaExpression right = fromTree(binaryTree.getRightOperand());
return new BinaryOperation(TreeUtils.typeOf(tree), tree.getKind(), left, right);
default:
result = null;
}
if (result == null) {
result = new Unknown(tree);
}
return result;
}
use of com.sun.source.tree.NewArrayTree in project checker-framework by typetools.
the class AnnotatedTypeFactory method getFunctionalInterfaceType.
/**
* Get the AnnotatedDeclaredType for the FunctionalInterface from assignment context of the method
* reference or lambda expression which may be a variable assignment, a method call, or a cast.
*
* <p>The assignment context is not always correct, so we must search up the AST. It will
* recursively search for lambdas nested in lambdas.
*
* @param tree the tree of the lambda or method reference
* @return the functional interface type or an uninferred type argument
*/
private AnnotatedTypeMirror getFunctionalInterfaceType(Tree tree) {
Tree parentTree = getPath(tree).getParentPath().getLeaf();
switch(parentTree.getKind()) {
case PARENTHESIZED:
return getFunctionalInterfaceType(parentTree);
case TYPE_CAST:
TypeCastTree cast = (TypeCastTree) parentTree;
assert isFunctionalInterface(trees.getTypeMirror(getPath(cast.getType())), parentTree, tree);
AnnotatedTypeMirror castATM = getAnnotatedType(cast.getType());
if (castATM.getKind() == TypeKind.INTERSECTION) {
AnnotatedIntersectionType itype = (AnnotatedIntersectionType) castATM;
for (AnnotatedTypeMirror t : itype.directSupertypes()) {
if (TypesUtils.isFunctionalInterface(t.getUnderlyingType(), getProcessingEnv())) {
return t;
}
}
// and would have raised an error already.
throw new BugInCF("Expected the type of a cast tree in an assignment context to contain a functional" + " interface bound. Found type: %s for tree: %s in lambda tree: %s", castATM, cast, tree);
}
return castATM;
case NEW_CLASS:
NewClassTree newClass = (NewClassTree) parentTree;
int indexOfLambda = newClass.getArguments().indexOf(tree);
ParameterizedExecutableType con = this.constructorFromUse(newClass);
AnnotatedTypeMirror constructorParam = AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(con.executableType, indexOfLambda);
assert isFunctionalInterface(constructorParam.getUnderlyingType(), parentTree, tree);
return constructorParam;
case NEW_ARRAY:
NewArrayTree newArray = (NewArrayTree) parentTree;
AnnotatedArrayType newArrayATM = getAnnotatedType(newArray);
AnnotatedTypeMirror elementATM = newArrayATM.getComponentType();
assert isFunctionalInterface(elementATM.getUnderlyingType(), parentTree, tree);
return elementATM;
case METHOD_INVOCATION:
MethodInvocationTree method = (MethodInvocationTree) parentTree;
int index = method.getArguments().indexOf(tree);
ParameterizedExecutableType exe = this.methodFromUse(method);
AnnotatedTypeMirror param = AnnotatedTypes.getAnnotatedTypeMirrorOfParameter(exe.executableType, index);
if (param.getKind() == TypeKind.WILDCARD) {
// param is an uninferred wildcard.
TypeMirror typeMirror = TreeUtils.typeOf(tree);
param = AnnotatedTypeMirror.createType(typeMirror, this, false);
addDefaultAnnotations(param);
}
assert isFunctionalInterface(param.getUnderlyingType(), parentTree, tree);
return param;
case VARIABLE:
VariableTree varTree = (VariableTree) parentTree;
assert isFunctionalInterface(TreeUtils.typeOf(varTree), parentTree, tree);
return getAnnotatedType(varTree.getType());
case ASSIGNMENT:
AssignmentTree assignmentTree = (AssignmentTree) parentTree;
assert isFunctionalInterface(TreeUtils.typeOf(assignmentTree), parentTree, tree);
return getAnnotatedType(assignmentTree.getVariable());
case RETURN:
Tree enclosing = TreePathUtil.enclosingOfKind(getPath(parentTree), new HashSet<>(Arrays.asList(Tree.Kind.METHOD, Tree.Kind.LAMBDA_EXPRESSION)));
if (enclosing.getKind() == Tree.Kind.METHOD) {
MethodTree enclosingMethod = (MethodTree) enclosing;
return getAnnotatedType(enclosingMethod.getReturnType());
} else {
LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) enclosing;
AnnotatedExecutableType methodExe = getFunctionTypeFromTree(enclosingLambda);
return methodExe.getReturnType();
}
case LAMBDA_EXPRESSION:
LambdaExpressionTree enclosingLambda = (LambdaExpressionTree) parentTree;
AnnotatedExecutableType methodExe = getFunctionTypeFromTree(enclosingLambda);
return methodExe.getReturnType();
case CONDITIONAL_EXPRESSION:
ConditionalExpressionTree conditionalExpressionTree = (ConditionalExpressionTree) parentTree;
final AnnotatedTypeMirror falseType = getAnnotatedType(conditionalExpressionTree.getFalseExpression());
final AnnotatedTypeMirror trueType = getAnnotatedType(conditionalExpressionTree.getTrueExpression());
// Known cases where we must use LUB because falseType/trueType will not be equal:
// a) when one of the types is a type variable that extends a functional interface
// or extends a type variable that extends a functional interface
// b) When one of the two sides of the expression is a reference to a sub-interface.
// e.g. interface ConsumeStr {
// public void consume(String s)
// }
// interface SubConsumer extends ConsumeStr {
// default void someOtherMethod() { ... }
// }
// SubConsumer s = ...;
// ConsumeStr stringConsumer = (someCondition) ? s : System.out::println;
AnnotatedTypeMirror conditionalType = AnnotatedTypes.leastUpperBound(this, trueType, falseType);
assert isFunctionalInterface(conditionalType.getUnderlyingType(), parentTree, tree);
return conditionalType;
default:
throw new BugInCF("Could not find functional interface from assignment context. " + "Unexpected tree type: " + parentTree.getKind() + " For lambda tree: " + tree);
}
}
use of com.sun.source.tree.NewArrayTree in project checker-framework by typetools.
the class CFGTranslationPhaseOne method convertCallArguments.
/**
* Given a method element and as list of argument expressions, return a list of {@link Node}s
* representing the arguments converted for a call of the method. This method applies to both
* method invocations and constructor calls.
*
* @param method an ExecutableElement representing a method to be called
* @param actualExprs a List of argument expressions to a call
* @return a List of {@link Node}s representing arguments after conversions required by a call to
* this method
*/
protected List<Node> convertCallArguments(ExecutableElement method, List<? extends ExpressionTree> actualExprs) {
// NOTE: It is important to convert one method argument before generating CFG nodes for the next
// argument, since label binding expects nodes to be generated in execution order. Therefore,
// this method first determines which conversions need to be applied and then iterates over the
// actual arguments.
List<? extends VariableElement> formals = method.getParameters();
int numFormals = formals.size();
ArrayList<Node> convertedNodes = new ArrayList<>(numFormals);
int numActuals = actualExprs.size();
if (method.isVarArgs()) {
// Create a new array argument if the actuals outnumber the formals, or if the last actual is
// not assignable to the last formal.
int lastArgIndex = numFormals - 1;
TypeMirror lastParamType = formals.get(lastArgIndex).asType();
if (numActuals == numFormals && types.isAssignable(TreeUtils.typeOf(actualExprs.get(numActuals - 1)), lastParamType)) {
// invocation conversion to all arguments.
for (int i = 0; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
if (actualVal == null) {
throw new BugInCF("CFGBuilder: scan returned null for %s [%s]", actualExprs.get(i), actualExprs.get(i).getClass());
}
convertedNodes.add(methodInvocationConvert(actualVal, formals.get(i).asType()));
}
} else {
assert lastParamType instanceof ArrayType : "variable argument formal must be an array";
// to initialize an array.
for (int i = 0; i < lastArgIndex; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(methodInvocationConvert(actualVal, formals.get(i).asType()));
}
TypeMirror elemType = ((ArrayType) lastParamType).getComponentType();
List<ExpressionTree> inits = new ArrayList<>(numActuals - lastArgIndex);
List<Node> initializers = new ArrayList<>(numActuals - lastArgIndex);
for (int i = lastArgIndex; i < numActuals; i++) {
inits.add(actualExprs.get(i));
Node actualVal = scan(actualExprs.get(i), null);
initializers.add(assignConvert(actualVal, elemType));
}
NewArrayTree wrappedVarargs = treeBuilder.buildNewArray(elemType, inits);
handleArtificialTree(wrappedVarargs);
Node lastArgument = new ArrayCreationNode(wrappedVarargs, lastParamType, /*dimensions=*/
Collections.emptyList(), initializers);
extendWithNode(lastArgument);
convertedNodes.add(lastArgument);
}
} else {
for (int i = 0; i < numActuals; i++) {
Node actualVal = scan(actualExprs.get(i), null);
convertedNodes.add(methodInvocationConvert(actualVal, formals.get(i).asType()));
}
}
return convertedNodes;
}
Aggregations