[groovy] return true type of a list literal in initializer position

Handle list literals in array context individually in the generator.
Extract new method isCastNeeded(), use Java assignability rules.
This commit is contained in:
Daniil Ovchinnikov
2017-04-14 21:43:31 +03:00
parent c76c4619e7
commit 22f7ac033a
5 changed files with 55 additions and 36 deletions
@@ -16,11 +16,13 @@
package org.jetbrains.plugins.groovy.lang.typing
import com.intellij.openapi.util.RecursionManager
import com.intellij.psi.*
import com.intellij.psi.CommonClassNames
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiType
import com.intellij.psi.util.InheritanceUtil.isInheritor
import com.intellij.psi.util.PsiUtil.substituteTypeParameter
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration
import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil
@@ -34,7 +36,7 @@ class DefaultListOrMapTypeCalculator : GrTypeCalculator<GrListOrMap> {
return getMapTypeFromDiamond(expression) ?: GrMapType.createFromNamedArgs(expression, expression.namedArguments)
}
else {
return getArrayTypeFromDeclaration(expression) ?: getListTypeFromDiamond(expression) ?: getTupleType(expression)
return getListTypeFromDiamond(expression) ?: getTupleType(expression)
}
}
@@ -74,11 +76,6 @@ class DefaultListOrMapTypeCalculator : GrTypeCalculator<GrListOrMap> {
)
}
private fun getArrayTypeFromDeclaration(expression: GrListOrMap): PsiType? {
val parent = expression.parent?.parent as? GrVariableDeclaration
return parent?.typeElementGroovy?.type as? PsiArrayType
}
private fun getTupleType(expression: GrListOrMap): PsiType? {
val initializers = expression.initializers
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,19 +90,21 @@ public class ExpressionGenerator extends Generator {
private final GroovyPsiElementFactory factory;
private final ExpressionContext context;
private final @Nullable PsiType expectedType;
public ExpressionGenerator(StringBuilder builder, ExpressionContext context) {
public ExpressionGenerator(StringBuilder builder, ExpressionContext context, @Nullable PsiType expectedType) {
this.builder = builder;
this.context = context;
this.expectedType = expectedType;
this.factory = GroovyPsiElementFactory.getInstance(context.project);
}
factory = GroovyPsiElementFactory.getInstance(context.project);
public ExpressionGenerator(StringBuilder builder, ExpressionContext context) {
this(builder, context, null);
}
public ExpressionGenerator(ExpressionContext context) {
this.builder = new StringBuilder();
this.context = context;
factory = GroovyPsiElementFactory.getInstance(context.project);
this(new StringBuilder(), context);
}
@Override
@@ -1172,16 +1174,14 @@ public class ExpressionGenerator extends Generator {
}
else {
TypeWriter.writeTypeForNew(builder, type, typeCastExpression);
builder.append('{');
final PsiType newExpectedType = expectedType instanceof PsiArrayType ? ((PsiArrayType)expectedType).getComponentType() : null;
final ExpressionGenerator childGenerator = new ExpressionGenerator(builder, context, newExpectedType);
for (GrExpression initializer : initializers) {
initializer.accept(this);
initializer.accept(childGenerator);
builder.append(", ");
}
if (initializers.length > 0) {
builder.delete(builder.length() - 2, builder.length());
//builder.removeFromTheEnd(2);
}
builder.delete(builder.length() - 2, builder.length());
builder.append('}');
}
return;
@@ -1407,8 +1407,7 @@ public class ExpressionGenerator extends Generator {
public void visitListOrMap(@NotNull GrListOrMap listOrMap) {
final PsiType type = listOrMap.getType();
//can be PsiArrayType or GrLiteralClassType
LOG.assertTrue(type instanceof GrLiteralClassType || type instanceof PsiArrayType || type instanceof PsiClassType);
LOG.assertTrue(type instanceof GrLiteralClassType || type instanceof PsiClassType);
if (listOrMap.isMap()) {
if (listOrMap.getNamedArguments().length == 0) {
@@ -1448,13 +1447,16 @@ public class ExpressionGenerator extends Generator {
}
}
private static PsiType getTypeToUseByList(GrListOrMap listOrMap, PsiType type) {
private PsiType getTypeToUseByList(GrListOrMap listOrMap, PsiType type) {
if (isImplicitlyCastedToArray(listOrMap)) {
PsiType iterable = ClosureParameterEnhancer.findTypeForIteration(listOrMap, listOrMap);
if (iterable != null) {
return new PsiArrayType(iterable);
}
}
else if (expectedType instanceof PsiArrayType) {
return expectedType;
}
if (type instanceof PsiClassType) {
PsiClass resolved = ((PsiClassType)type).resolve();
if (resolved != null && CommonClassNames.JAVA_UTIL_LIST.equals(resolved.getQualifiedName())) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
@@ -33,6 +34,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
@@ -40,6 +42,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaratio
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
@@ -55,6 +58,7 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProces
import org.jetbrains.plugins.groovy.refactoring.DefaultGroovyVariableNameValidator;
import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -434,22 +438,42 @@ public class GenerationUtil {
}
builder.append('(');
}
final PsiType iType = getDeclaredType(initializer, expressionContext);
//generate cast
if (original != null && iType != null && !TypesUtil.isAssignable(original, iType, initializer)) {
if (isCastNeeded(original, initializer, expressionContext)) {
builder.append('(');
TypeWriter.writeType(builder, original, initializer);
builder.append(')');
}
initializer.accept(new ExpressionGenerator(builder, expressionContext));
initializer.accept(new ExpressionGenerator(builder, expressionContext, original));
if (wrapped) {
builder.append(')');
}
}
}
private static boolean isCastNeeded(PsiType target, GrExpression initializer, ExpressionContext expressionContext) {
if (target == null) return false;
final PsiType iType = getDeclaredType(initializer, expressionContext);
if (iType == null) return false;
if (TypeConversionUtil.isAssignable(target, iType)) return false;
if (initializer instanceof GrLiteral) {
Object value = ((GrLiteral)initializer).getValue();
if (value instanceof BigDecimal && Double.isFinite(((BigDecimal)value).doubleValue())) {
return !TypeConversionUtil.isAssignable(target, PsiType.DOUBLE);
}
else if (value instanceof String && ((String)value).length() == 1) {
return !PsiType.CHAR.equals(PsiPrimitiveType.getOptionallyUnboxedType(target));
}
}
else if (initializer instanceof GrListOrMap && target instanceof PsiArrayType) {
GrListOrMap listOrMap = (GrListOrMap)initializer;
return listOrMap.isMap();
}
return true;
}
static void writeVariableSeparately(GrVariable variable, StringBuilder builder, ExpressionContext expressionContext) {
PsiType type = expressionContext.typeProvider.getVarType(variable);
ModifierListGenerator.writeModifiers(builder, variable.getModifierList());
@@ -583,12 +607,6 @@ public class GenerationUtil {
final GrExpression invokedExpression = ((GrMethodCall)expression).getInvokedExpression();
return getDeclaredType(invokedExpression, context);
}
else if (expression instanceof GrBinaryExpression) {
final GroovyResolveResult result = PsiImplUtil.extractUniqueResult(((GrBinaryExpression)expression).multiResolve(false));
if (result.getElement() instanceof PsiMethod) {
return getDeclaredType((PsiMethod)result.getElement(), result.getSubstitutor(), context);
}
}
else if (expression instanceof GrIndexProperty) {
final GroovyResolveResult result = ((GrIndexProperty)expression).advancedResolve();
if (result.getElement() instanceof PsiMethod) {
@@ -15,4 +15,5 @@
*/
def list = []
int[] arr = []
List<String> strings = []
List<String> strings = []
int[] arr2 = list
@@ -1,3 +1,4 @@
java.util.List list = new java.util.ArrayList();
java.lang.Integer[] arr = new java.lang.Integer[0];
java.util.List<java.lang.String> strings = new java.util.ArrayList<java.lang.String>();
java.lang.Integer[] arr2 = (java.lang.Integer[])list;