[groovy] LUB of numeric types (IDEA-134930)

This commit is contained in:
Daniil Ovchinnikov
2016-11-18 19:40:54 +03:00
parent 096640c64e
commit 271357d4ef
2 changed files with 164 additions and 16 deletions
@@ -29,7 +29,6 @@ import com.intellij.util.containers.ComparatorUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashMap;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
@@ -54,6 +53,7 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.HardcodedGroovyMethodConstants.*;
@@ -179,6 +179,15 @@ public class TypesUtil {
TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_NUMBER, 9);
}
private static final List<PsiType> LUB_NUMERIC_TYPES = ContainerUtil.newArrayList(
PsiType.BYTE,
PsiType.SHORT,
PsiType.INT,
PsiType.LONG,
PsiType.FLOAT,
PsiType.DOUBLE
);
static {
ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BOOLEAN, PsiType.BOOLEAN);
ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BYTE, PsiType.BYTE);
@@ -191,21 +200,6 @@ public class TypesUtil {
ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_VOID, PsiType.VOID);
}
private static final TIntObjectHashMap<String> RANK_TO_TYPE = new TIntObjectHashMap<>();
static {
RANK_TO_TYPE.put(1, CommonClassNames.JAVA_LANG_INTEGER);
RANK_TO_TYPE.put(2, CommonClassNames.JAVA_LANG_INTEGER);
RANK_TO_TYPE.put(3, CommonClassNames.JAVA_LANG_INTEGER);
RANK_TO_TYPE.put(4, CommonClassNames.JAVA_LANG_LONG);
RANK_TO_TYPE.put(5, GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER);
RANK_TO_TYPE.put(6, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL);
RANK_TO_TYPE.put(7, CommonClassNames.JAVA_LANG_DOUBLE);
RANK_TO_TYPE.put(8, CommonClassNames.JAVA_LANG_DOUBLE);
RANK_TO_TYPE.put(9, CommonClassNames.JAVA_LANG_NUMBER);
}
/**
* @deprecated see {@link #canAssign}
*/
@@ -499,6 +493,10 @@ public class TypesUtil {
@Nullable
public static PsiType getLeastUpperBound(@NotNull PsiType type1, @NotNull PsiType type2, PsiManager manager) {
{
PsiType numericLUB = getNumericLUB(type1, type2);
if (numericLUB != null) return numericLUB;
}
if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) {
GrTupleType tuple1 = (GrTupleType)type1;
GrTupleType tuple2 = (GrTupleType)type2;
@@ -565,6 +563,21 @@ public class TypesUtil {
return GenericsUtil.getLeastUpperBound(type1, type2, manager);
}
@Nullable
private static PsiType getNumericLUB(@Nullable PsiType type1, @Nullable PsiType type2) {
PsiPrimitiveType unboxedType1 = PsiPrimitiveType.getUnboxedType(type1);
PsiPrimitiveType unboxedType2 = PsiPrimitiveType.getUnboxedType(type2);
if (unboxedType1 != null && unboxedType2 != null) {
int i1 = LUB_NUMERIC_TYPES.indexOf(unboxedType1);
int i2 = LUB_NUMERIC_TYPES.indexOf(unboxedType2);
if (i1 >= 0 && i2 >= 0) {
if (i1 > i2) return type1;
if (i2 > i1) return type2;
}
}
return null;
}
private static boolean checkEmptyListAndList(PsiType type1, PsiType type2) {
if (type1 instanceof GrTupleType) {
PsiType[] types = ((GrTupleType)type1).getComponentTypes();
@@ -0,0 +1,135 @@
/*
* Copyright 2000-2016 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.
* 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 org.jetbrains.plugins.groovy.lang.resolve
import com.intellij.testFramework.LightProjectDescriptor
import groovy.transform.CompileStatic
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
import org.jetbrains.plugins.groovy.LightGroovyTestCase
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
@CompileStatic
class GrNumericLUBTypeTest extends LightGroovyTestCase {
final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
@Override
void setUp() throws Exception {
super.setUp()
fixture.addFileToProject '''constants.groovy''', '''\
interface Constants {
Byte nB = 0
Short nS = 0
Integer nI = 0
Long nL = 0
Float nF = 0
Double nD = 0
}
'''
}
private void doTest(String expressionText, String expectedType) {
def file = fixture.configureByText('_.groovy', """\
import static Constants.*
$expressionText
""") as GroovyFile
def expression = file.statements.last() as GrExpression
assert expression.type.equalsToText(expectedType): "'$expression.text' : $expression.type"
}
void 'test elvis types'() {
[
// Byte
'nB ?: nB': 'java.lang.Byte',
'nB ?: nS': 'java.lang.Short',
'nB ?: nI': 'java.lang.Integer',
'nB ?: nL': 'java.lang.Long',
'nB ?: nF': 'java.lang.Float',
'nB ?: nD': 'java.lang.Double',
// Short
'nS ?: nB': 'java.lang.Short',
'nS ?: nS': 'java.lang.Short',
'nS ?: nI': 'java.lang.Integer',
'nS ?: nL': 'java.lang.Long',
'nS ?: nF': 'java.lang.Float',
'nS ?: nD': 'java.lang.Double',
// Integer
'nI ?: nB': 'java.lang.Integer',
'nI ?: nS': 'java.lang.Integer',
'nI ?: nI': 'java.lang.Integer',
'nI ?: nL': 'java.lang.Long',
'nI ?: nF': 'java.lang.Float',
'nI ?: nD': 'java.lang.Double',
// Long
'nL ?: nB': 'java.lang.Long',
'nL ?: nS': 'java.lang.Long',
'nL ?: nI': 'java.lang.Long',
'nL ?: nL': 'java.lang.Long',
'nL ?: nF': 'java.lang.Float',
'nL ?: nD': 'java.lang.Double',
// Float
'nF ?: nB': 'java.lang.Float',
'nF ?: nS': 'java.lang.Float',
'nF ?: nI': 'java.lang.Float',
'nF ?: nL': 'java.lang.Float',
'nF ?: nF': 'java.lang.Float',
'nF ?: nD': 'java.lang.Double',
// Double
'nD ?: nB': 'java.lang.Double',
'nD ?: nS': 'java.lang.Double',
'nD ?: nI': 'java.lang.Double',
'nD ?: nL': 'java.lang.Double',
'nD ?: nF': 'java.lang.Double',
'nD ?: nD': 'java.lang.Double',
].each {
doTest it.key, it.value
}
}
// same as elvis
void 'test ternary types'() {
[
'42 ? nB : nB': 'java.lang.Byte',
'42 ? nB : nS': 'java.lang.Short',
'42 ? nB : nI': 'java.lang.Integer',
'42 ? nB : nL': 'java.lang.Long',
'42 ? nB : nF': 'java.lang.Float',
'42 ? nB : nD': 'java.lang.Double',
].each {
doTest it.key, it.value
}
}
// same as elvis
void 'test if branches assignment'() {
[
'def a; if (42) { a = nB } else { a = nB }; a': 'java.lang.Byte',
'def a; if (42) { a = nB } else { a = nS }; a': 'java.lang.Short',
'def a; if (42) { a = nB } else { a = nI }; a': 'java.lang.Integer',
'def a; if (42) { a = nB } else { a = nL }; a': 'java.lang.Long',
'def a; if (42) { a = nB } else { a = nF }; a': 'java.lang.Float',
'def a; if (42) { a = nB } else { a = nD }; a': 'java.lang.Double',
].each {
doTest it.key, it.value
}
}
}