/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.klint.detector.api;
import static com.android.tools.klint.client.api.JavaParser.TYPE_BOOLEAN;
import static com.android.tools.klint.client.api.JavaParser.TYPE_BYTE;
import static com.android.tools.klint.client.api.JavaParser.TYPE_CHAR;
import static com.android.tools.klint.client.api.JavaParser.TYPE_DOUBLE;
import static com.android.tools.klint.client.api.JavaParser.TYPE_FLOAT;
import static com.android.tools.klint.client.api.JavaParser.TYPE_INT;
import static com.android.tools.klint.client.api.JavaParser.TYPE_LONG;
import static com.android.tools.klint.client.api.JavaParser.TYPE_OBJECT;
import static com.android.tools.klint.client.api.JavaParser.TYPE_SHORT;
import static com.android.tools.klint.client.api.JavaParser.TYPE_STRING;
import static com.android.tools.klint.detector.api.JavaContext.getParentOfType;
import static org.jetbrains.uast.UastBinaryExpressionWithTypeKind.TYPE_CAST;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.klint.client.api.JavaParser.ResolvedField;
import com.android.tools.klint.client.api.JavaParser.ResolvedNode;
import com.android.tools.klint.client.api.UastLintUtils;
import com.google.common.collect.Lists;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiArrayInitializerExpression;
import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiBinaryExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiConditionalExpression;
import com.intellij.psi.PsiDeclarationStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiLiteral;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiParenthesizedExpression;
import com.intellij.psi.PsiPrefixExpression;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeCastExpression;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.PsiVariable;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.uast.*;
import org.jetbrains.uast.UReferenceExpression;
import org.jetbrains.uast.util.UastExpressionUtils;
import org.jetbrains.uast.visitor.AbstractUastVisitor;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import lombok.ast.ArrayCreation;
import lombok.ast.ArrayInitializer;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
import lombok.ast.BooleanLiteral;
import lombok.ast.Cast;
import lombok.ast.CharLiteral;
import lombok.ast.Expression;
import lombok.ast.ExpressionStatement;
import lombok.ast.FloatingPointLiteral;
import lombok.ast.InlineIfExpression;
import lombok.ast.IntegralLiteral;
import lombok.ast.Node;
import lombok.ast.NullLiteral;
import lombok.ast.Select;
import lombok.ast.Statement;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.TypeReference;
import lombok.ast.UnaryExpression;
import lombok.ast.UnaryOperator;
import lombok.ast.VariableDeclaration;
import lombok.ast.VariableDefinition;
import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
/** Evaluates constant expressions */
public class ConstantEvaluator {
private final JavaContext mContext;
private boolean mAllowUnknown;
/**
* Creates a new constant evaluator
*
* @param context the context to use to resolve field references, if any
*/
public ConstantEvaluator(@Nullable JavaContext context) {
mContext = context;
}
/**
* Whether we allow computing values where some terms are unknown. For example, the expression
* {@code "foo" + x + "bar"} would return {@code null} without and {@code "foobar"} with.
*
* @return this for constructor chaining
*/
public ConstantEvaluator allowUnknowns() {
mAllowUnknown = true;
return this;
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any
*
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
* @deprecated Use {@link #evaluate(PsiElement)} instead
*/
@Deprecated
@Nullable
public Object evaluate(@NonNull Node node) {
if (node instanceof NullLiteral) {
return null;
} else if (node instanceof BooleanLiteral) {
return ((BooleanLiteral)node).astValue();
} else if (node instanceof StringLiteral) {
StringLiteral string = (StringLiteral) node;
return string.astValue();
} else if (node instanceof CharLiteral) {
return ((CharLiteral)node).astValue();
} else if (node instanceof IntegralLiteral) {
IntegralLiteral literal = (IntegralLiteral) node;
// Don't combine to ?: since that will promote astIntValue to a long
if (literal.astMarkedAsLong()) {
return literal.astLongValue();
} else {
return literal.astIntValue();
}
} else if (node instanceof FloatingPointLiteral) {
FloatingPointLiteral literal = (FloatingPointLiteral) node;
// Don't combine to ?: since that will promote astFloatValue to a double
if (literal.astMarkedAsFloat()) {
return literal.astFloatValue();
} else {
return literal.astDoubleValue();
}
} else if (node instanceof UnaryExpression) {
UnaryOperator operator = ((UnaryExpression) node).astOperator();
Object operand = evaluate(((UnaryExpression) node).astOperand());
if (operand == null) {
return null;
}
switch (operator) {
case LOGICAL_NOT:
if (operand instanceof Boolean) {
return !(Boolean) operand;
}
break;
case UNARY_PLUS:
return operand;
case BINARY_NOT:
if (operand instanceof Integer) {
return ~(Integer) operand;
} else if (operand instanceof Long) {
return ~(Long) operand;
} else if (operand instanceof Short) {
return ~(Short) operand;
} else if (operand instanceof Character) {
return ~(Character) operand;
} else if (operand instanceof Byte) {
return ~(Byte) operand;
}
break;
case UNARY_MINUS:
if (operand instanceof Integer) {
return -(Integer) operand;
} else if (operand instanceof Long) {
return -(Long) operand;
} else if (operand instanceof Double) {
return -(Double) operand;
} else if (operand instanceof Float) {
return -(Float) operand;
} else if (operand instanceof Short) {
return -(Short) operand;
} else if (operand instanceof Character) {
return -(Character) operand;
} else if (operand instanceof Byte) {
return -(Byte) operand;
}
break;
}
} else if (node instanceof InlineIfExpression) {
InlineIfExpression expression = (InlineIfExpression) node;
Object known = evaluate(expression.astCondition());
if (known == Boolean.TRUE && expression.astIfTrue() != null) {
return evaluate(expression.astIfTrue());
} else if (known == Boolean.FALSE && expression.astIfFalse() != null) {
return evaluate(expression.astIfFalse());
}
} else if (node instanceof BinaryExpression) {
BinaryOperator operator = ((BinaryExpression) node).astOperator();
Object operandLeft = evaluate(((BinaryExpression) node).astLeft());
Object operandRight = evaluate(((BinaryExpression) node).astRight());
if (operandLeft == null || operandRight == null) {
if (mAllowUnknown) {
if (operandLeft == null) {
return operandRight;
} else {
return operandLeft;
}
}
return null;
}
if (operandLeft instanceof String && operandRight instanceof String) {
if (operator == BinaryOperator.PLUS) {
return operandLeft.toString() + operandRight.toString();
}
return null;
} else if (operandLeft instanceof Boolean && operandRight instanceof Boolean) {
boolean left = (Boolean) operandLeft;
boolean right = (Boolean) operandRight;
switch (operator) {
case LOGICAL_OR:
return left || right;
case LOGICAL_AND:
return left && right;
case BITWISE_OR:
return left | right;
case BITWISE_XOR:
return left ^ right;
case BITWISE_AND:
return left & right;
case EQUALS:
return left == right;
case NOT_EQUALS:
return left != right;
}
} else if (operandLeft instanceof Number && operandRight instanceof Number) {
Number left = (Number) operandLeft;
Number right = (Number) operandRight;
boolean isInteger =
!(left instanceof Float || left instanceof Double
|| right instanceof Float || right instanceof Double);
boolean isWide =
isInteger ? (left instanceof Long || right instanceof Long)
: (left instanceof Double || right instanceof Double);
switch (operator) {
case BITWISE_OR:
if (isWide) {
return left.longValue() | right.longValue();
} else {
return left.intValue() | right.intValue();
}
case BITWISE_XOR:
if (isWide) {
return left.longValue() ^ right.longValue();
} else {
return left.intValue() ^ right.intValue();
}
case BITWISE_AND:
if (isWide) {
return left.longValue() & right.longValue();
} else {
return left.intValue() & right.intValue();
}
case EQUALS:
if (isInteger) {
return left.longValue() == right.longValue();
} else {
return left.doubleValue() == right.doubleValue();
}
case NOT_EQUALS:
if (isInteger) {
return left.longValue() != right.longValue();
} else {
return left.doubleValue() != right.doubleValue();
}
case GREATER:
if (isInteger) {
return left.longValue() > right.longValue();
} else {
return left.doubleValue() > right.doubleValue();
}
case GREATER_OR_EQUAL:
if (isInteger) {
return left.longValue() >= right.longValue();
} else {
return left.doubleValue() >= right.doubleValue();
}
case LESS:
if (isInteger) {
return left.longValue() < right.longValue();
} else {
return left.doubleValue() < right.doubleValue();
}
case LESS_OR_EQUAL:
if (isInteger) {
return left.longValue() <= right.longValue();
} else {
return left.doubleValue() <= right.doubleValue();
}
case SHIFT_LEFT:
if (isWide) {
return left.longValue() << right.intValue();
} else {
return left.intValue() << right.intValue();
}
case SHIFT_RIGHT:
if (isWide) {
return left.longValue() >> right.intValue();
} else {
return left.intValue() >> right.intValue();
}
case BITWISE_SHIFT_RIGHT:
if (isWide) {
return left.longValue() >>> right.intValue();
} else {
return left.intValue() >>> right.intValue();
}
case PLUS:
if (isInteger) {
if (isWide) {
return left.longValue() + right.longValue();
} else {
return left.intValue() + right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() + right.doubleValue();
} else {
return left.floatValue() + right.floatValue();
}
}
case MINUS:
if (isInteger) {
if (isWide) {
return left.longValue() - right.longValue();
} else {
return left.intValue() - right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() - right.doubleValue();
} else {
return left.floatValue() - right.floatValue();
}
}
case MULTIPLY:
if (isInteger) {
if (isWide) {
return left.longValue() * right.longValue();
} else {
return left.intValue() * right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() * right.doubleValue();
} else {
return left.floatValue() * right.floatValue();
}
}
case DIVIDE:
if (isInteger) {
if (isWide) {
return left.longValue() / right.longValue();
} else {
return left.intValue() / right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() / right.doubleValue();
} else {
return left.floatValue() / right.floatValue();
}
}
case REMAINDER:
if (isInteger) {
if (isWide) {
return left.longValue() % right.longValue();
} else {
return left.intValue() % right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() % right.doubleValue();
} else {
return left.floatValue() % right.floatValue();
}
}
default:
return null;
}
}
} else if (node instanceof Cast) {
Cast cast = (Cast)node;
Object operandValue = evaluate(cast.astOperand());
if (operandValue instanceof Number) {
Number number = (Number)operandValue;
String typeName = cast.astTypeReference().getTypeName();
if (typeName.equals("float")) {
return number.floatValue();
} else if (typeName.equals("double")) {
return number.doubleValue();
} else if (typeName.equals("int")) {
return number.intValue();
} else if (typeName.equals("long")) {
return number.longValue();
} else if (typeName.equals("short")) {
return number.shortValue();
} else if (typeName.equals("byte")) {
return number.byteValue();
}
}
return operandValue;
} else if (mContext != null && (node instanceof VariableReference ||
node instanceof Select)) {
ResolvedNode resolved = mContext.resolve(node);
if (resolved instanceof ResolvedField) {
ResolvedField field = (ResolvedField) resolved;
Object value = field.getValue();
if (value != null) {
return value;
}
Node astNode = field.findAstNode();
if (astNode instanceof VariableDeclaration) {
VariableDeclaration declaration = (VariableDeclaration) astNode;
VariableDefinition definition = declaration.astDefinition();
if (definition != null && definition.astModifiers().isFinal()) {
StrictListAccessor<VariableDefinitionEntry, VariableDefinition> variables =
definition.astVariables();
if (variables.size() == 1) {
VariableDefinitionEntry first = variables.first();
if (first.astInitializer() != null) {
return evaluate(first.astInitializer());
}
}
}
}
return null;
} else if (node instanceof VariableReference) {
Statement statement = getParentOfType(node, Statement.class, false);
if (statement != null) {
ListIterator<Node> iterator = statement.getParent().getChildren().listIterator();
while (iterator.hasNext()) {
if (iterator.next() == statement) {
if (iterator.hasPrevious()) { // should always be true
iterator.previous();
}
break;
}
}
String targetName = ((VariableReference)node).astIdentifier().astValue();
while (iterator.hasPrevious()) {
Node previous = iterator.previous();
if (previous instanceof VariableDeclaration) {
VariableDeclaration declaration = (VariableDeclaration) previous;
VariableDefinition definition = declaration.astDefinition();
for (VariableDefinitionEntry entry : definition
.astVariables()) {
if (entry.astInitializer() != null
&& entry.astName().astValue().equals(targetName)) {
return evaluate(entry.astInitializer());
}
}
} else if (previous instanceof ExpressionStatement) {
ExpressionStatement expressionStatement = (ExpressionStatement) previous;
Expression expression = expressionStatement.astExpression();
if (expression instanceof BinaryExpression &&
((BinaryExpression) expression).astOperator()
== BinaryOperator.ASSIGN) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
if (targetName.equals(binaryExpression.astLeft().toString())) {
return evaluate(binaryExpression.astRight());
}
}
}
}
}
}
} else if (node instanceof ArrayCreation) {
ArrayCreation creation = (ArrayCreation) node;
ArrayInitializer initializer = creation.astInitializer();
if (initializer != null) {
TypeReference typeReference = creation.astComponentTypeReference();
StrictListAccessor<Expression, ArrayInitializer> expressions = initializer
.astExpressions();
List<Object> values = Lists.newArrayListWithExpectedSize(expressions.size());
Class<?> commonType = null;
for (Expression expression : expressions) {
Object value = evaluate(expression);
if (value != null) {
values.add(value);
if (commonType == null) {
commonType = value.getClass();
} else {
while (!commonType.isAssignableFrom(value.getClass())) {
commonType = commonType.getSuperclass();
}
}
} else if (!mAllowUnknown) {
// Inconclusive
return null;
}
}
if (!values.isEmpty()) {
Object o = Array.newInstance(commonType, values.size());
return values.toArray((Object[]) o);
} else if (mContext != null) {
ResolvedNode type = mContext.resolve(typeReference);
System.out.println(type);
// TODO: return new array of this type
}
} else {
// something like "new byte[3]" but with no initializer.
String type = creation.astComponentTypeReference().toString();
// TODO: Look up the size and only if small, use it. E.g. if it was byte[3]
// we could return a byte[3] array, but if it's say byte[1024*1024] we don't
// want to do that.
int size = 0;
if (TYPE_BYTE.equals(type)) {
return new byte[size];
}
if (TYPE_BOOLEAN.equals(type)) {
return new boolean[size];
}
if (TYPE_INT.equals(type)) {
return new int[size];
}
if (TYPE_LONG.equals(type)) {
return new long[size];
}
if (TYPE_CHAR.equals(type)) {
return new char[size];
}
if (TYPE_FLOAT.equals(type)) {
return new float[size];
}
if (TYPE_DOUBLE.equals(type)) {
return new double[size];
}
if (TYPE_STRING.equals(type)) {
//noinspection SSBasedInspection
return new String[size];
}
if (TYPE_SHORT.equals(type)) {
return new short[size];
}
if (TYPE_OBJECT.equals(type)) {
//noinspection SSBasedInspection
return new Object[size];
}
}
}
// TODO: Check for MethodInvocation and perform some common operations -
// Math.* methods, String utility methods like notNullize, etc
return null;
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any
*
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
*/
@Nullable
public Object evaluate(@Nullable UElement node) {
if (node == null) {
return null;
}
if (node instanceof ULiteralExpression) {
return ((ULiteralExpression) node).getValue();
} else if (node instanceof UPrefixExpression) {
UastPrefixOperator operator = ((UPrefixExpression) node).getOperator();
Object operand = evaluate(((UPrefixExpression) node).getOperand());
if (operand == null) {
return null;
}
if (operator == UastPrefixOperator.LOGICAL_NOT) {
if (operand instanceof Boolean) {
return !(Boolean) operand;
}
} else if (operator == UastPrefixOperator.UNARY_PLUS) {
return operand;
} else if (operator == UastPrefixOperator.BITWISE_NOT) {
if (operand instanceof Integer) {
return ~(Integer) operand;
} else if (operand instanceof Long) {
return ~(Long) operand;
} else if (operand instanceof Short) {
return ~(Short) operand;
} else if (operand instanceof Character) {
return ~(Character) operand;
} else if (operand instanceof Byte) {
return ~(Byte) operand;
}
} else if (operator == UastPrefixOperator.UNARY_MINUS) {
if (operand instanceof Integer) {
return -(Integer) operand;
} else if (operand instanceof Long) {
return -(Long) operand;
} else if (operand instanceof Double) {
return -(Double) operand;
} else if (operand instanceof Float) {
return -(Float) operand;
} else if (operand instanceof Short) {
return -(Short) operand;
} else if (operand instanceof Character) {
return -(Character) operand;
} else if (operand instanceof Byte) {
return -(Byte) operand;
}
}
} else if (node instanceof UIfExpression
&& ((UIfExpression) node).getExpressionType() != null) {
UIfExpression expression = (UIfExpression) node;
Object known = evaluate(expression.getCondition());
if (known == Boolean.TRUE && expression.getThenExpression() != null) {
return evaluate(expression.getThenExpression());
} else if (known == Boolean.FALSE && expression.getElseExpression() != null) {
return evaluate(expression.getElseExpression());
}
} else if (node instanceof UParenthesizedExpression) {
UParenthesizedExpression parenthesizedExpression = (UParenthesizedExpression) node;
UExpression expression = parenthesizedExpression.getExpression();
return evaluate(expression);
} else if (node instanceof UBinaryExpression) {
UastBinaryOperator operator = ((UBinaryExpression) node).getOperator();
Object operandLeft = evaluate(((UBinaryExpression) node).getLeftOperand());
Object operandRight = evaluate(((UBinaryExpression) node).getRightOperand());
if (operandLeft == null || operandRight == null) {
if (mAllowUnknown) {
if (operandLeft == null) {
return operandRight;
} else {
return operandLeft;
}
}
return null;
}
if (operandLeft instanceof String && operandRight instanceof String) {
if (operator == UastBinaryOperator.PLUS) {
return operandLeft.toString() + operandRight.toString();
}
return null;
} else if (operandLeft instanceof Boolean && operandRight instanceof Boolean) {
boolean left = (Boolean) operandLeft;
boolean right = (Boolean) operandRight;
if (operator == UastBinaryOperator.LOGICAL_OR) {
return left || right;
} else if (operator == UastBinaryOperator.LOGICAL_AND) {
return left && right;
} else if (operator == UastBinaryOperator.BITWISE_OR) {
return left | right;
} else if (operator == UastBinaryOperator.BITWISE_XOR) {
return left ^ right;
} else if (operator == UastBinaryOperator.BITWISE_AND) {
return left & right;
} else if (operator == UastBinaryOperator.IDENTITY_EQUALS
|| operator == UastBinaryOperator.EQUALS) {
return left == right;
} else if (operator == UastBinaryOperator.IDENTITY_NOT_EQUALS
|| operator == UastBinaryOperator.NOT_EQUALS) {
return left != right;
}
} else if (operandLeft instanceof Number && operandRight instanceof Number) {
Number left = (Number) operandLeft;
Number right = (Number) operandRight;
boolean isInteger =
!(left instanceof Float || left instanceof Double
|| right instanceof Float || right instanceof Double);
boolean isWide =
isInteger ? (left instanceof Long || right instanceof Long)
: (left instanceof Double || right instanceof Double);
if (operator == UastBinaryOperator.BITWISE_OR) {
if (isWide) {
return left.longValue() | right.longValue();
} else {
return left.intValue() | right.intValue();
}
} else if (operator == UastBinaryOperator.BITWISE_XOR) {
if (isWide) {
return left.longValue() ^ right.longValue();
} else {
return left.intValue() ^ right.intValue();
}
} else if (operator == UastBinaryOperator.BITWISE_AND) {
if (isWide) {
return left.longValue() & right.longValue();
} else {
return left.intValue() & right.intValue();
}
} else if (operator == UastBinaryOperator.EQUALS
|| operator == UastBinaryOperator.IDENTITY_EQUALS) {
if (isInteger) {
return left.longValue() == right.longValue();
} else {
return left.doubleValue() == right.doubleValue();
}
} else if (operator == UastBinaryOperator.NOT_EQUALS
|| operator == UastBinaryOperator.IDENTITY_NOT_EQUALS) {
if (isInteger) {
return left.longValue() != right.longValue();
} else {
return left.doubleValue() != right.doubleValue();
}
} else if (operator == UastBinaryOperator.GREATER) {
if (isInteger) {
return left.longValue() > right.longValue();
} else {
return left.doubleValue() > right.doubleValue();
}
} else if (operator == UastBinaryOperator.GREATER_OR_EQUALS) {
if (isInteger) {
return left.longValue() >= right.longValue();
} else {
return left.doubleValue() >= right.doubleValue();
}
} else if (operator == UastBinaryOperator.LESS) {
if (isInteger) {
return left.longValue() < right.longValue();
} else {
return left.doubleValue() < right.doubleValue();
}
} else if (operator == UastBinaryOperator.LESS_OR_EQUALS) {
if (isInteger) {
return left.longValue() <= right.longValue();
} else {
return left.doubleValue() <= right.doubleValue();
}
} else if (operator == UastBinaryOperator.SHIFT_LEFT) {
if (isWide) {
return left.longValue() << right.intValue();
} else {
return left.intValue() << right.intValue();
}
} else if (operator == UastBinaryOperator.SHIFT_RIGHT) {
if (isWide) {
return left.longValue() >> right.intValue();
} else {
return left.intValue() >> right.intValue();
}
} else if (operator == UastBinaryOperator.UNSIGNED_SHIFT_RIGHT) {
if (isWide) {
return left.longValue() >>> right.intValue();
} else {
return left.intValue() >>> right.intValue();
}
} else if (operator == UastBinaryOperator.PLUS) {
if (isInteger) {
if (isWide) {
return left.longValue() + right.longValue();
} else {
return left.intValue() + right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() + right.doubleValue();
} else {
return left.floatValue() + right.floatValue();
}
}
} else if (operator == UastBinaryOperator.MINUS) {
if (isInteger) {
if (isWide) {
return left.longValue() - right.longValue();
} else {
return left.intValue() - right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() - right.doubleValue();
} else {
return left.floatValue() - right.floatValue();
}
}
} else if (operator == UastBinaryOperator.MULTIPLY) {
if (isInteger) {
if (isWide) {
return left.longValue() * right.longValue();
} else {
return left.intValue() * right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() * right.doubleValue();
} else {
return left.floatValue() * right.floatValue();
}
}
} else if (operator == UastBinaryOperator.DIV) {
if (isInteger) {
if (isWide) {
return left.longValue() / right.longValue();
} else {
return left.intValue() / right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() / right.doubleValue();
} else {
return left.floatValue() / right.floatValue();
}
}
} else if (operator == UastBinaryOperator.MOD) {
if (isInteger) {
if (isWide) {
return left.longValue() % right.longValue();
} else {
return left.intValue() % right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() % right.doubleValue();
} else {
return left.floatValue() % right.floatValue();
}
}
} else {
return null;
}
}
} else if (node instanceof UBinaryExpressionWithType &&
((UBinaryExpressionWithType) node).getOperationKind() == TYPE_CAST) {
UBinaryExpressionWithType cast = (UBinaryExpressionWithType) node;
Object operandValue = evaluate(cast.getOperand());
if (operandValue instanceof Number) {
Number number = (Number) operandValue;
PsiType type = cast.getType();
if (PsiType.FLOAT.equals(type)) {
return number.floatValue();
} else if (PsiType.DOUBLE.equals(type)) {
return number.doubleValue();
} else if (PsiType.INT.equals(type)) {
return number.intValue();
} else if (PsiType.LONG.equals(type)) {
return number.longValue();
} else if (PsiType.SHORT.equals(type)) {
return number.shortValue();
} else if (PsiType.BYTE.equals(type)) {
return number.byteValue();
}
}
return operandValue;
} else if (node instanceof UReferenceExpression) {
PsiElement resolved = ((UReferenceExpression) node).resolve();
if (resolved instanceof PsiVariable) {
PsiVariable variable = (PsiVariable) resolved;
Object value = UastLintUtils.findLastValue(variable, node, mContext, this);
if (value != null) {
return value;
}
if (variable.getInitializer() != null) {
return evaluate(variable.getInitializer());
}
return null;
}
} else if (UastExpressionUtils.isNewArrayWithDimensions((UExpression) node)) {
UCallExpression call = (UCallExpression) node;
PsiType arrayType = call.getExpressionType();
if (arrayType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType) arrayType).getComponentType();
// Single-dimension array
if (!(componentType instanceof PsiArrayType)
&& call.getValueArgumentCount() == 1) {
Object lengthObj = evaluate(call.getValueArguments().get(0));
if (lengthObj instanceof Number) {
int length = ((Number) lengthObj).intValue();
if (length > 30) {
length = 30;
}
if (componentType == PsiType.BOOLEAN) {
return new boolean[length];
} else if (isObjectType(componentType)) {
return new Object[length];
} else if (componentType == PsiType.CHAR) {
return new char[length];
} else if (componentType == PsiType.BYTE) {
return new byte[length];
} else if (componentType == PsiType.DOUBLE) {
return new double[length];
} else if (componentType == PsiType.FLOAT) {
return new float[length];
} else if (componentType == PsiType.INT) {
return new int[length];
} else if (componentType == PsiType.SHORT) {
return new short[length];
} else if (componentType == PsiType.LONG) {
return new long[length];
} else if (isStringType(componentType)) {
return new String[length];
}
}
}
}
} else if (UastExpressionUtils.isNewArrayWithInitializer(node)) {
UCallExpression call = (UCallExpression) node;
PsiType arrayType = call.getExpressionType();
if (arrayType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType) arrayType).getComponentType();
// Single-dimension array
if (!(componentType instanceof PsiArrayType)) {
int length = call.getValueArgumentCount();
List<Object> evaluatedArgs = new ArrayList<Object>(length);
for (UExpression arg : call.getValueArguments()) {
Object evaluatedArg = evaluate(arg);
if (!mAllowUnknown && evaluatedArg == null) {
// Inconclusive
return null;
}
evaluatedArgs.add(evaluatedArg);
}
if (componentType == PsiType.BOOLEAN) {
boolean[] arr = new boolean[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Boolean) {
arr[i] = (Boolean) o;
}
}
return arr;
} else if (isObjectType(componentType)) {
Object[] arr = new Object[length];
for (int i = 0; i < length; ++i) {
arr[i] = evaluatedArgs.get(i);
}
return arr;
} else if (componentType.equals(PsiType.CHAR)) {
char[] arr = new char[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Character) {
arr[i] = (Character) o;
}
}
return arr;
} else if (componentType.equals(PsiType.BYTE)) {
byte[] arr = new byte[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Byte) {
arr[i] = (Byte) o;
}
}
return arr;
} else if (componentType.equals(PsiType.DOUBLE)) {
double[] arr = new double[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Double) {
arr[i] = (Double) o;
}
}
return arr;
} else if (componentType.equals(PsiType.FLOAT)) {
float[] arr = new float[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Float) {
arr[i] = (Float) o;
}
}
return arr;
} else if (componentType.equals(PsiType.INT)) {
int[] arr = new int[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Integer) {
arr[i] = (Integer) o;
}
}
return arr;
} else if (componentType.equals(PsiType.SHORT)) {
short[] arr = new short[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Short) {
arr[i] = (Short) o;
}
}
return arr;
} else if (componentType.equals(PsiType.LONG)) {
long[] arr = new long[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof Long) {
arr[i] = (Long) o;
}
}
return arr;
} else if (isStringType(componentType)) {
String[] arr = new String[length];
for (int i = 0; i < length; ++i) {
Object o = evaluatedArgs.get(i);
if (o instanceof String) {
arr[i] = (String) o;
}
}
return arr;
}
}
}
}
if (node instanceof UExpression) {
Object evaluated = ((UExpression) node).evaluate();
if (evaluated != null) {
return evaluated;
}
}
// TODO: Check for MethodInvocation and perform some common operations -
// Math.* methods, String utility methods like notNullize, etc
return null;
}
private static boolean isStringType(PsiType type) {
if (!(type instanceof PsiClassType)) {
return false;
}
PsiClass resolvedClass = ((PsiClassType) type).resolve();
return resolvedClass != null && TYPE_STRING.equals(resolvedClass.getQualifiedName());
}
private static boolean isObjectType(PsiType type) {
if (!(type instanceof PsiClassType)) {
return false;
}
PsiClass resolvedClass = ((PsiClassType) type).resolve();
return resolvedClass != null && TYPE_OBJECT.equals(resolvedClass.getQualifiedName());
}
public static class LastAssignmentFinder extends AbstractUastVisitor {
private final PsiVariable mVariable;
private final UElement mEndAt;
private final ConstantEvaluator mConstantEvaluator;
private boolean mDone = false;
private int mCurrentLevel = 0;
private int mVariableLevel = -1;
private Object mCurrentValue;
private UElement mLastAssignment;
public LastAssignmentFinder(
@NonNull PsiVariable variable,
@NonNull UElement endAt,
@NonNull JavaContext context,
@Nullable ConstantEvaluator constantEvaluator,
int variableLevel) {
mVariable = variable;
mEndAt = endAt;
UExpression initializer = context.getUastContext().getInitializerBody(variable);
mLastAssignment = initializer;
mConstantEvaluator = constantEvaluator;
if (initializer != null && constantEvaluator != null) {
mCurrentValue = constantEvaluator.evaluate(initializer);
}
this.mVariableLevel = variableLevel;
}
@Nullable
public Object getCurrentValue() {
return mCurrentValue;
}
@Nullable
public UElement getLastAssignment() {
return mLastAssignment;
}
@Override
public boolean visitElement(UElement node) {
if (elementHasLevel(node)) {
mCurrentLevel++;
}
if (node.equals(mEndAt)) {
mDone = true;
}
return mDone || super.visitElement(node);
}
@Override
public boolean visitVariable(UVariable node) {
if (mVariableLevel < 0 && node.getPsi().isEquivalentTo(mVariable)) {
mVariableLevel = mCurrentLevel;
}
return super.visitVariable(node);
}
@Override
public void afterVisitBinaryExpression(UBinaryExpression node) {
if (!mDone
&& node.getOperator() instanceof UastBinaryOperator.AssignOperator
&& mVariableLevel >= 0) {
UExpression leftOperand = node.getLeftOperand();
UastBinaryOperator operator = node.getOperator();
if (!(operator instanceof UastBinaryOperator.AssignOperator)
|| !(leftOperand instanceof UResolvable)) {
return;
}
PsiElement resolved = ((UResolvable) leftOperand).resolve();
if (!mVariable.equals(resolved)) {
return;
}
// Last assignment is unknown if we see an assignment inside
// some conditional or loop statement.
if (mCurrentLevel > mVariableLevel + 1) {
mLastAssignment = null;
mCurrentValue = null;
return;
}
UExpression rightOperand = node.getRightOperand();
ConstantEvaluator constantEvaluator = mConstantEvaluator;
mCurrentValue = (constantEvaluator != null)
? constantEvaluator.evaluate(rightOperand)
: null;
mLastAssignment = rightOperand;
}
super.afterVisitBinaryExpression(node);
}
@Override
public void afterVisitElement(UElement node) {
if (elementHasLevel(node)) {
mCurrentLevel--;
}
super.afterVisitElement(node);
}
private static boolean elementHasLevel(UElement node) {
return !(node instanceof UBlockExpression
|| node instanceof UDeclarationsExpression);
}
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any
*
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
*/
@Nullable
public Object evaluate(@Nullable PsiElement node) {
if (node == null) {
return null;
}
if (node instanceof PsiLiteral) {
return ((PsiLiteral)node).getValue();
} else if (node instanceof PsiPrefixExpression) {
IElementType operator = ((PsiPrefixExpression) node).getOperationTokenType();
Object operand = evaluate(((PsiPrefixExpression) node).getOperand());
if (operand == null) {
return null;
}
if (operator == JavaTokenType.EXCL) {
if (operand instanceof Boolean) {
return !(Boolean) operand;
}
} else if (operator == JavaTokenType.PLUS) {
return operand;
} else if (operator == JavaTokenType.TILDE) {
if (operand instanceof Integer) {
return ~(Integer) operand;
} else if (operand instanceof Long) {
return ~(Long) operand;
} else if (operand instanceof Short) {
return ~(Short) operand;
} else if (operand instanceof Character) {
return ~(Character) operand;
} else if (operand instanceof Byte) {
return ~(Byte) operand;
}
} else if (operator == JavaTokenType.MINUS) {
if (operand instanceof Integer) {
return -(Integer) operand;
} else if (operand instanceof Long) {
return -(Long) operand;
} else if (operand instanceof Double) {
return -(Double) operand;
} else if (operand instanceof Float) {
return -(Float) operand;
} else if (operand instanceof Short) {
return -(Short) operand;
} else if (operand instanceof Character) {
return -(Character) operand;
} else if (operand instanceof Byte) {
return -(Byte) operand;
}
}
} else if (node instanceof PsiConditionalExpression) {
PsiConditionalExpression expression = (PsiConditionalExpression) node;
Object known = evaluate(expression.getCondition());
if (known == Boolean.TRUE && expression.getThenExpression() != null) {
return evaluate(expression.getThenExpression());
} else if (known == Boolean.FALSE && expression.getElseExpression() != null) {
return evaluate(expression.getElseExpression());
}
} else if (node instanceof PsiParenthesizedExpression) {
PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) node;
PsiExpression expression = parenthesizedExpression.getExpression();
if (expression != null) {
return evaluate(expression);
}
} else if (node instanceof PsiBinaryExpression) {
IElementType operator = ((PsiBinaryExpression) node).getOperationTokenType();
Object operandLeft = evaluate(((PsiBinaryExpression) node).getLOperand());
Object operandRight = evaluate(((PsiBinaryExpression) node).getROperand());
if (operandLeft == null || operandRight == null) {
if (mAllowUnknown) {
if (operandLeft == null) {
return operandRight;
} else {
return operandLeft;
}
}
return null;
}
if (operandLeft instanceof String && operandRight instanceof String) {
if (operator == JavaTokenType.PLUS) {
return operandLeft.toString() + operandRight.toString();
}
return null;
} else if (operandLeft instanceof Boolean && operandRight instanceof Boolean) {
boolean left = (Boolean) operandLeft;
boolean right = (Boolean) operandRight;
if (operator == JavaTokenType.OROR) {
return left || right;
} else if (operator == JavaTokenType.ANDAND) {
return left && right;
} else if (operator == JavaTokenType.OR) {
return left | right;
} else if (operator == JavaTokenType.XOR) {
return left ^ right;
} else if (operator == JavaTokenType.AND) {
return left & right;
} else if (operator == JavaTokenType.EQEQ) {
return left == right;
} else if (operator == JavaTokenType.NE) {
return left != right;
}
} else if (operandLeft instanceof Number && operandRight instanceof Number) {
Number left = (Number) operandLeft;
Number right = (Number) operandRight;
boolean isInteger =
!(left instanceof Float || left instanceof Double
|| right instanceof Float || right instanceof Double);
boolean isWide =
isInteger ? (left instanceof Long || right instanceof Long)
: (left instanceof Double || right instanceof Double);
if (operator == JavaTokenType.OR) {
if (isWide) {
return left.longValue() | right.longValue();
} else {
return left.intValue() | right.intValue();
}
} else if (operator == JavaTokenType.XOR) {
if (isWide) {
return left.longValue() ^ right.longValue();
} else {
return left.intValue() ^ right.intValue();
}
} else if (operator == JavaTokenType.AND) {
if (isWide) {
return left.longValue() & right.longValue();
} else {
return left.intValue() & right.intValue();
}
} else if (operator == JavaTokenType.EQEQ) {
if (isInteger) {
return left.longValue() == right.longValue();
} else {
return left.doubleValue() == right.doubleValue();
}
} else if (operator == JavaTokenType.NE) {
if (isInteger) {
return left.longValue() != right.longValue();
} else {
return left.doubleValue() != right.doubleValue();
}
} else if (operator == JavaTokenType.GT) {
if (isInteger) {
return left.longValue() > right.longValue();
} else {
return left.doubleValue() > right.doubleValue();
}
} else if (operator == JavaTokenType.GE) {
if (isInteger) {
return left.longValue() >= right.longValue();
} else {
return left.doubleValue() >= right.doubleValue();
}
} else if (operator == JavaTokenType.LT) {
if (isInteger) {
return left.longValue() < right.longValue();
} else {
return left.doubleValue() < right.doubleValue();
}
} else if (operator == JavaTokenType.LE) {
if (isInteger) {
return left.longValue() <= right.longValue();
} else {
return left.doubleValue() <= right.doubleValue();
}
} else if (operator == JavaTokenType.LTLT) {
if (isWide) {
return left.longValue() << right.intValue();
} else {
return left.intValue() << right.intValue();
}
} else if (operator == JavaTokenType.GTGT) {
if (isWide) {
return left.longValue() >> right.intValue();
} else {
return left.intValue() >> right.intValue();
}
} else if (operator == JavaTokenType.GTGTGT) {
if (isWide) {
return left.longValue() >>> right.intValue();
} else {
return left.intValue() >>> right.intValue();
}
} else if (operator == JavaTokenType.PLUS) {
if (isInteger) {
if (isWide) {
return left.longValue() + right.longValue();
} else {
return left.intValue() + right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() + right.doubleValue();
} else {
return left.floatValue() + right.floatValue();
}
}
} else if (operator == JavaTokenType.MINUS) {
if (isInteger) {
if (isWide) {
return left.longValue() - right.longValue();
} else {
return left.intValue() - right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() - right.doubleValue();
} else {
return left.floatValue() - right.floatValue();
}
}
} else if (operator == JavaTokenType.ASTERISK) {
if (isInteger) {
if (isWide) {
return left.longValue() * right.longValue();
} else {
return left.intValue() * right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() * right.doubleValue();
} else {
return left.floatValue() * right.floatValue();
}
}
} else if (operator == JavaTokenType.DIV) {
if (isInteger) {
if (isWide) {
return left.longValue() / right.longValue();
} else {
return left.intValue() / right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() / right.doubleValue();
} else {
return left.floatValue() / right.floatValue();
}
}
} else if (operator == JavaTokenType.PERC) {
if (isInteger) {
if (isWide) {
return left.longValue() % right.longValue();
} else {
return left.intValue() % right.intValue();
}
} else {
if (isWide) {
return left.doubleValue() % right.doubleValue();
} else {
return left.floatValue() % right.floatValue();
}
}
} else {
return null;
}
}
} else if (node instanceof PsiTypeCastExpression) {
PsiTypeCastExpression cast = (PsiTypeCastExpression) node;
Object operandValue = evaluate(cast.getOperand());
if (operandValue instanceof Number) {
Number number = (Number) operandValue;
PsiTypeElement typeElement = cast.getCastType();
if (typeElement != null) {
PsiType type = typeElement.getType();
if (PsiType.FLOAT.equals(type)) {
return number.floatValue();
} else if (PsiType.DOUBLE.equals(type)) {
return number.doubleValue();
} else if (PsiType.INT.equals(type)) {
return number.intValue();
} else if (PsiType.LONG.equals(type)) {
return number.longValue();
} else if (PsiType.SHORT.equals(type)) {
return number.shortValue();
} else if (PsiType.BYTE.equals(type)) {
return number.byteValue();
}
}
}
return operandValue;
} else if (node instanceof PsiReference) {
PsiElement resolved = ((PsiReference) node).resolve();
if (resolved instanceof PsiField) {
PsiField field = (PsiField) resolved;
Object value = field.computeConstantValue();
if (value != null) {
return value;
}
if (field.getInitializer() != null) {
return evaluate(field.getInitializer());
}
return null;
} else if (resolved instanceof PsiLocalVariable) {
PsiLocalVariable variable = (PsiLocalVariable) resolved;
PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class,
false);
if (statement != null) {
PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
PsiStatement.class);
String targetName = variable.getName();
if (targetName == null) {
return null;
}
while (prev != null) {
if (prev instanceof PsiDeclarationStatement) {
for (PsiElement element : ((PsiDeclarationStatement) prev)
.getDeclaredElements()) {
if (variable.equals(element)) {
return evaluate(variable.getInitializer());
}
}
} else if (prev instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement) prev)
.getExpression();
if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assign
= (PsiAssignmentExpression) expression;
PsiExpression lhs = assign.getLExpression();
if (lhs instanceof PsiReferenceExpression) {
PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
if (targetName.equals(reference.getReferenceName()) &&
reference.getQualifier() == null) {
return evaluate(assign.getRExpression());
}
}
}
}
prev = PsiTreeUtil.getPrevSiblingOfType(prev,
PsiStatement.class);
}
}
}
} else if (node instanceof PsiNewExpression) {
PsiNewExpression creation = (PsiNewExpression) node;
PsiArrayInitializerExpression initializer = creation.getArrayInitializer();
PsiType type = creation.getType();
if (type instanceof PsiArrayType) {
if (initializer != null) {
PsiExpression[] initializers = initializer.getInitializers();
Class<?> commonType = null;
List<Object> values = Lists.newArrayListWithExpectedSize(initializers.length);
int count = 0;
for (PsiExpression expression : initializers) {
Object value = evaluate(expression);
if (value != null) {
values.add(value);
if (commonType == null) {
commonType = value.getClass();
} else {
while (!commonType.isAssignableFrom(value.getClass())) {
commonType = commonType.getSuperclass();
}
}
} else if (!mAllowUnknown) {
// Inconclusive
return null;
}
count++;
if (count == 20) { // avoid large initializers
break;
}
}
type = type.getDeepComponentType();
if (type == PsiType.INT) {
if (!values.isEmpty()) {
int[] array = new int[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Integer) {
array[i] = (Integer) o;
}
}
return array;
}
return new int[0];
} else if (type == PsiType.BOOLEAN) {
if (!values.isEmpty()) {
boolean[] array = new boolean[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Boolean) {
array[i] = (Boolean) o;
}
}
return array;
}
return new boolean[0];
} else if (type == PsiType.DOUBLE) {
if (!values.isEmpty()) {
double[] array = new double[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Double) {
array[i] = (Double) o;
}
}
return array;
}
return new double[0];
} else if (type == PsiType.LONG) {
if (!values.isEmpty()) {
long[] array = new long[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Long) {
array[i] = (Long) o;
}
}
return array;
}
return new long[0];
} else if (type == PsiType.FLOAT) {
if (!values.isEmpty()) {
float[] array = new float[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Float) {
array[i] = (Float) o;
}
}
return array;
}
return new float[0];
} else if (type == PsiType.CHAR) {
if (!values.isEmpty()) {
char[] array = new char[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Character) {
array[i] = (Character) o;
}
}
return array;
}
return new char[0];
} else if (type == PsiType.BYTE) {
if (!values.isEmpty()) {
byte[] array = new byte[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Byte) {
array[i] = (Byte) o;
}
}
return array;
}
return new byte[0];
} else if (type == PsiType.SHORT) {
if (!values.isEmpty()) {
short[] array = new short[values.size()];
for (int i = 0; i < values.size(); i++) {
Object o = values.get(i);
if (o instanceof Short) {
array[i] = (Short) o;
}
}
return array;
}
return new short[0];
} else {
if (!values.isEmpty()) {
Object o = Array.newInstance(commonType, values.size());
return values.toArray((Object[]) o);
}
return null;
}
} else {
// something like "new byte[3]" but with no initializer.
// Look up the size and only if small, use it. E.g. if it was byte[3]
// we return a byte[3] array, but if it's say byte[1024*1024] we don't
// want to do that.
PsiExpression[] arrayDimensions = creation.getArrayDimensions();
int size = 0;
if (arrayDimensions.length == 1) {
Object fixedSize = evaluate(arrayDimensions[0]);
if (fixedSize instanceof Number) {
size = ((Number)fixedSize).intValue();
if (size > 30) {
size = 30;
}
}
}
type = type.getDeepComponentType();
if (type instanceof PsiPrimitiveType) {
if (PsiType.BYTE.equals(type)) {
return new byte[size];
}
if (PsiType.BOOLEAN.equals(type)) {
return new boolean[size];
}
if (PsiType.INT.equals(type)) {
return new int[size];
}
if (PsiType.LONG.equals(type)) {
return new long[size];
}
if (PsiType.CHAR.equals(type)) {
return new char[size];
}
if (PsiType.FLOAT.equals(type)) {
return new float[size];
}
if (PsiType.DOUBLE.equals(type)) {
return new double[size];
}
if (PsiType.SHORT.equals(type)) {
return new short[size];
}
} else if (type instanceof PsiClassType) {
String className = type.getCanonicalText();
if (TYPE_STRING.equals(className)) {
//noinspection SSBasedInspection
return new String[size];
}
if (TYPE_OBJECT.equals(className)) {
//noinspection SSBasedInspection
return new Object[size];
}
}
}
}
}
// TODO: Check for MethodInvocation and perform some common operations -
// Math.* methods, String utility methods like notNullize, etc
return null;
}
/**
* Returns true if the node is pointing to a an array literal
*/
public static boolean isArrayLiteral(@Nullable UElement node, @NonNull JavaContext context) {
if (node instanceof UReferenceExpression) {
PsiElement resolved = ((UReferenceExpression) node).resolve();
if (resolved instanceof PsiVariable) {
PsiVariable variable = (PsiVariable) resolved;
UExpression lastAssignment =
UastLintUtils.findLastAssignment(variable, node, context);
if (lastAssignment != null) {
return isArrayLiteral(lastAssignment, context);
}
}
} else if (UastExpressionUtils.isNewArrayWithDimensions(node)) {
return true;
} else if (UastExpressionUtils.isNewArrayWithInitializer(node)) {
return true;
} else if (node instanceof UParenthesizedExpression) {
UParenthesizedExpression parenthesizedExpression = (UParenthesizedExpression) node;
UExpression expression = parenthesizedExpression.getExpression();
return isArrayLiteral(expression, context);
} else if (UastExpressionUtils.isTypeCast(node)) {
UBinaryExpressionWithType castExpression = (UBinaryExpressionWithType) node;
assert castExpression != null;
UExpression operand = castExpression.getOperand();
return isArrayLiteral(operand, context);
}
return false;
}
/**
* Returns true if the node is pointing to a an array literal
*/
public static boolean isArrayLiteral(@Nullable PsiElement node) {
if (node instanceof PsiReference) {
PsiElement resolved = ((PsiReference) node).resolve();
if (resolved instanceof PsiField) {
PsiField field = (PsiField) resolved;
if (field.getInitializer() != null) {
return isArrayLiteral(field.getInitializer());
}
} else if (resolved instanceof PsiLocalVariable) {
PsiLocalVariable variable = (PsiLocalVariable) resolved;
PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class,
false);
if (statement != null) {
PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
PsiStatement.class);
String targetName = variable.getName();
if (targetName == null) {
return false;
}
while (prev != null) {
if (prev instanceof PsiDeclarationStatement) {
for (PsiElement element : ((PsiDeclarationStatement) prev)
.getDeclaredElements()) {
if (variable.equals(element)) {
return isArrayLiteral(variable.getInitializer());
}
}
} else if (prev instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement) prev)
.getExpression();
if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assign
= (PsiAssignmentExpression) expression;
PsiExpression lhs = assign.getLExpression();
if (lhs instanceof PsiReferenceExpression) {
PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
if (targetName.equals(reference.getReferenceName()) &&
reference.getQualifier() == null) {
return isArrayLiteral(assign.getRExpression());
}
}
}
}
prev = PsiTreeUtil.getPrevSiblingOfType(prev,
PsiStatement.class);
}
}
}
} else if (node instanceof PsiNewExpression) {
PsiNewExpression creation = (PsiNewExpression) node;
if (creation.getArrayInitializer() != null) {
return true;
}
PsiType type = creation.getType();
if (type instanceof PsiArrayType) {
return true;
}
} else if (node instanceof PsiParenthesizedExpression) {
PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) node;
PsiExpression expression = parenthesizedExpression.getExpression();
if (expression != null) {
return isArrayLiteral(expression);
}
} else if (node instanceof PsiTypeCastExpression) {
PsiTypeCastExpression castExpression = (PsiTypeCastExpression) node;
PsiExpression operand = castExpression.getOperand();
if (operand != null) {
return isArrayLiteral(operand);
}
}
return false;
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
* @deprecated Use {@link #evaluate(JavaContext, PsiElement)} instead
*/
@Deprecated
@Nullable
public static Object evaluate(@NonNull JavaContext context, @NonNull Node node) {
return new ConstantEvaluator(context).evaluate(node);
}
/**
* Evaluates the given node and returns the constant string it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result if the result is a string.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @param allowUnknown whether we should construct the string even if some parts of it are
* unknown
* @return the corresponding string, if any
* @deprecated Use {@link #evaluateString(JavaContext, PsiElement, boolean)} instead
*/
@Deprecated
@Nullable
public static String evaluateString(@NonNull JavaContext context, @NonNull Node node,
boolean allowUnknown) {
ConstantEvaluator evaluator = new ConstantEvaluator(context);
if (allowUnknown) {
evaluator.allowUnknowns();
}
Object value = evaluator.evaluate(node);
return value instanceof String ? (String) value : null;
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
*/
@Nullable
public static Object evaluate(@Nullable JavaContext context, @NonNull PsiElement node) {
return new ConstantEvaluator(context).evaluate(node);
}
/**
* Evaluates the given node and returns the constant value it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @return the corresponding constant value - a String, an Integer, a Float, and so on
*/
@Nullable
public static Object evaluate(@Nullable JavaContext context, @NonNull UElement node) {
return new ConstantEvaluator(context).evaluate(node);
}
/**
* Evaluates the given node and returns the constant string it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result if the result is a string.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @param allowUnknown whether we should construct the string even if some parts of it are
* unknown
* @return the corresponding string, if any
*/
@Nullable
public static String evaluateString(@Nullable JavaContext context, @NonNull PsiElement node,
boolean allowUnknown) {
ConstantEvaluator evaluator = new ConstantEvaluator(context);
if (allowUnknown) {
evaluator.allowUnknowns();
}
Object value = evaluator.evaluate(node);
return value instanceof String ? (String) value : null;
}
/**
* Evaluates the given node and returns the constant string it resolves to, if any. Convenience
* wrapper which creates a new {@linkplain ConstantEvaluator}, evaluates the node and returns
* the result if the result is a string.
*
* @param context the context to use to resolve field references, if any
* @param node the node to compute the constant value for
* @param allowUnknown whether we should construct the string even if some parts of it are
* unknown
* @return the corresponding string, if any
*/
@Nullable
public static String evaluateString(@Nullable JavaContext context, @NonNull UElement node,
boolean allowUnknown) {
ConstantEvaluator evaluator = new ConstantEvaluator(context);
if (allowUnknown) {
evaluator.allowUnknowns();
}
Object value = evaluator.evaluate(node);
return value instanceof String ? (String) value : null;
}
}