From 37af4a2d81620dcdb992162aceb477bec9627578 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 24 Jan 2017 22:54:07 +0300 Subject: [PATCH] [groovy] properly compute result of +,-,* on BigInteger-s (IDEA-139913) --- .../statements/expressions/TypeConstants.java | 8 +- .../statements/expressions/TypesUtil.java | 10 ++ .../GrBinaryExpressionUtil.java | 30 +++- .../lang/resolve/NumberMathTypingTest.groovy | 154 ++++++++++++++++++ .../lang/resolve/TypeInferenceTest.groovy | 18 +- 5 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NumberMathTypingTest.groovy diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypeConstants.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypeConstants.java index 0884a122ee52..217d4b7c7486 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypeConstants.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypeConstants.java @@ -20,8 +20,7 @@ import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiType; import org.jetbrains.annotations.Nullable; -import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.TYPE_TO_RANK; -import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.getQualifiedName; +import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.*; public interface TypeConstants { @@ -44,4 +43,9 @@ public interface TypeConstants { } return 0; } + + @Nullable + static String getTypeFqn(int rank) { + return RANK_TO_TYPE.get(rank); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java index 8a377b92fda0..364e2a191c45 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java @@ -29,6 +29,7 @@ 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; @@ -180,6 +181,15 @@ public class TypesUtil implements TypeConstants { TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_NUMBER, 10); } + static final TIntObjectHashMap RANK_TO_TYPE = new TIntObjectHashMap<>(); + + static { + TYPE_TO_RANK.forEachEntry((fqn, rank) -> { + RANK_TO_TYPE.put(rank, fqn); + return true; + }); + } + private static final List LUB_NUMERIC_TYPES = ContainerUtil.newArrayList( PsiType.BYTE, PsiType.SHORT, diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java index efd18cfe70d6..666699877661 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 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. @@ -18,19 +18,35 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiType; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; +import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypeConstants.*; +import static org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.createTypeByFQClassName; + /** * Created by Max Medvedev on 12/20/13 */ public class GrBinaryExpressionUtil { + private static final int[] RANKS = new int[]{ + INTEGER_RANK, LONG_RANK, BIG_INTEGER_RANK, BIG_DECIMAL_RANK, DOUBLE_RANK + }; + public static PsiType getDefaultNumericResultType(PsiType ltype, PsiType rtype, GrOperatorExpression e) { - if (isBigDecimal(ltype, rtype)) return createBigDecimal(e); - if (isFloatOrDouble(ltype, rtype)) return createDouble(e); - if (isLong(ltype, rtype)) return createLong(e); - return createInteger(e); + int lRank = getTypeRank(ltype); + int rRank = getTypeRank(rtype); + int resultRank = getResultTypeRank(lRank, rRank); + String fqn = getTypeFqn(resultRank); + return fqn == null ? null : createTypeByFQClassName(fqn, e); + } + + private static int getResultTypeRank(int lRank, int rRank) { + for (int rank : RANKS) { + if (lRank <= rank && rRank <= rank) { + return rank; + } + } + return 0; } public static PsiType createDouble(GrOperatorExpression e) { @@ -63,6 +79,6 @@ public class GrBinaryExpressionUtil { } public static PsiType getTypeByFQName(String fqn, GrOperatorExpression e) { - return TypesUtil.createTypeByFQClassName(fqn, e); + return createTypeByFQClassName(fqn, e); } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NumberMathTypingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NumberMathTypingTest.groovy new file mode 100644 index 000000000000..aaccd4386066 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/NumberMathTypingTest.groovy @@ -0,0 +1,154 @@ +/* + * 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. + * 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.psi.PsiClassType +import com.intellij.testFramework.LightProjectDescriptor +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 + +class NumberMathTypingTest extends LightGroovyTestCase { + + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + + @Override + void setUp() throws Exception { + super.setUp(); + addBigInteger() + addBigDecimal() + } + + void 'test +'() { + def data = [ + ['Byte', 'Byte', 'Integer'], + ['Byte', 'Character', 'Integer'], + ['Byte', 'Short', 'Integer'], + ['Byte', 'Integer', 'Integer'], + ['Byte', 'Long', 'Long'], + ['Byte', 'BigInteger', 'BigInteger'], + ['Byte', 'BigDecimal', 'BigDecimal'], + ['Byte', 'Float', 'Double'], + ['Byte', 'Double', 'Double'], + + ['Character', 'Byte', 'Integer'], + ['Character', 'Character', 'Integer'], + ['Character', 'Short', 'Integer'], + ['Character', 'Integer', 'Integer'], + ['Character', 'Long', 'Long'], + ['Character', 'BigInteger', 'BigInteger'], + ['Character', 'BigDecimal', 'BigDecimal'], + ['Character', 'Float', 'Double'], + ['Character', 'Double', 'Double'], + + ['Short', 'Byte', 'Integer'], + ['Short', 'Character', 'Integer'], + ['Short', 'Short', 'Integer'], + ['Short', 'Integer', 'Integer'], + ['Short', 'Long', 'Long'], + ['Short', 'BigInteger', 'BigInteger'], + ['Short', 'BigDecimal', 'BigDecimal'], + ['Short', 'Float', 'Double'], + ['Short', 'Double', 'Double'], + + ['Integer', 'Byte', 'Integer'], + ['Integer', 'Character', 'Integer'], + ['Integer', 'Short', 'Integer'], + ['Integer', 'Integer', 'Integer'], + ['Integer', 'Long', 'Long'], + ['Integer', 'BigInteger', 'BigInteger'], + ['Integer', 'BigDecimal', 'BigDecimal'], + ['Integer', 'Float', 'Double'], + ['Integer', 'Double', 'Double'], + + ['Long', 'Byte', 'Long'], + ['Long', 'Character', 'Long'], + ['Long', 'Short', 'Long'], + ['Long', 'Integer', 'Long'], + ['Long', 'Long', 'Long'], + ['Long', 'BigInteger', 'BigInteger'], + ['Long', 'BigDecimal', 'BigDecimal'], + ['Long', 'Float', 'Double'], + ['Long', 'Double', 'Double'], + + ['BigInteger', 'Byte', 'BigInteger'], + ['BigInteger', 'Character', 'BigInteger'], + ['BigInteger', 'Short', 'BigInteger'], + ['BigInteger', 'Integer', 'BigInteger'], + ['BigInteger', 'Long', 'BigInteger'], + ['BigInteger', 'BigInteger', 'BigInteger'], + ['BigInteger', 'BigDecimal', 'BigDecimal'], + ['BigInteger', 'Float', 'Double'], + ['BigInteger', 'Double', 'Double'], + + ['BigDecimal', 'Byte', 'BigDecimal'], + ['BigDecimal', 'Character', 'BigDecimal'], + ['BigDecimal', 'Short', 'BigDecimal'], + ['BigDecimal', 'Integer', 'BigDecimal'], + ['BigDecimal', 'Long', 'BigDecimal'], + ['BigDecimal', 'BigInteger', 'BigDecimal'], + ['BigDecimal', 'BigDecimal', 'BigDecimal'], + ['BigDecimal', 'Float', 'Double'], + ['BigDecimal', 'Double', 'Double'], + + ['Float', 'Byte', 'Double'], + ['Float', 'Character', 'Double'], + ['Float', 'Short', 'Double'], + ['Float', 'Integer', 'Double'], + ['Float', 'Long', 'Double'], + ['Float', 'BigInteger', 'Double'], + ['Float', 'BigDecimal', 'Double'], + ['Float', 'Float', 'Double'], + ['Float', 'Double', 'Double'], + + ['Double', 'Byte', 'Double'], + ['Double', 'Character', 'Double'], + ['Double', 'Short', 'Double'], + ['Double', 'Integer', 'Double'], + ['Double', 'Long', 'Double'], + ['Double', 'BigInteger', 'Double'], + ['Double', 'BigDecimal', 'Double'], + ['Double', 'Float', 'Double'], + ['Double', 'Double', 'Double'], + ] + for (row in data) { + def (left, right, type) = row + doTest left, '+', right, type + } + } + + private void doTest(String left, String operator, String right, String expected) { + fixture.with { + def file = configureByText('_.groovy', """\ +def foo($left a, $right b) { + a $operator b +} +""") as GroovyFile + try { + def expr = file.methods.first().block.statements.first() + assert expr instanceof GrExpression + def type = expr.type + assert type instanceof PsiClassType + def name = type.className + assert name == expected + } + catch (Throwable e) { + throw new RuntimeException("$left $operator $right (expected: $expected)", e) + } + } + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy index 8b802bdf705c..bc754d730cc1 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy @@ -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. @@ -614,22 +614,6 @@ class Any { ''', 'java.lang.String') } - void testPlus1() { - doExprTest('2+2', 'java.lang.Integer') - } - - void testPlus2() { - doExprTest('2f+2', 'java.lang.Double') - } - - void testPlus3() { - doExprTest('2f+2f', 'java.lang.Double') - } - - void testPlus4() { - doExprTest('2.5+2', 'java.math.BigDecimal') - } - void testMultiply1() { doExprTest('2*2', 'java.lang.Integer') }