blob: 92cb22802c6cb9b21deaeb739a4b2f90e6713007 [file] [log] [blame]
/*
* Copyright 2000-2014 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.PsiIntersectionType
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiType
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
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.api.statements.expressions.GrAssignmentExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType
import static com.intellij.psi.CommonClassNames.*
/**
* @author ven
*/
public class TypeInferenceTest extends TypeInferenceTestBase {
public void testTryFinallyFlow() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("tryFinallyFlow/A.groovy").element;
final PsiType type = ref.type;
assertTrue(type instanceof PsiIntersectionType);
final PsiType[] conjuncts = ((PsiIntersectionType)type).conjuncts;
assertEquals(conjuncts.length, 2);
}
public void testTryFinallyFlow1() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("tryFinallyFlow1/A.groovy").element;
final PsiType type = ref.type;
assertNotNull(type);
assertTrue(type.equalsToText("java.lang.Integer"));
}
public void testTryFinallyFlow2() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("tryFinallyFlow2/A.groovy").element;
final PsiType type = ref.type;
assertNotNull(type);
assertTrue(type.equalsToText("java.lang.Integer"));
}
public void testThrowVariable() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("throwVariable/A.groovy").element;
final PsiType type = ref.type;
assertNotNull(type);
assertEquals("java.lang.Exception", type.canonicalText);
}
public void testGrvy852() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("grvy852/A.groovy").element;
final PsiType type = ref.type;
assertNotNull(type);
assertEquals("java.lang.Object", type.canonicalText);
}
public void testGenericMethod() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("genericMethod/A.groovy").element;
final PsiType type = ref.type;
assertNotNull(type);
assertEquals("java.util.List<java.lang.String>", type.canonicalText);
}
public void testCircular() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("circular/A.groovy").element;
assertNull(ref.type);
}
public void testCircular1() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("circular1/A.groovy").element;
assertNull(ref.type);
}
public void testClosure() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("closure/A.groovy").element;
assertNotNull(ref.type);
}
public void testClosure1() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("closure1/A.groovy").element;
assertTrue(ref.type.equalsToText("java.lang.Integer"));
}
public void testClosure2() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("closure2/A.groovy").element;
assertTrue(ref.type.equalsToText("java.lang.Integer"));
}
public void testGrvy1209() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("grvy1209/A.groovy").element;
assertTrue(ref.type.equalsToText("java.lang.String"));
}
public void testLeastUpperBoundClosureType() {
GrReferenceExpression ref = (GrReferenceExpression)configureByFile("leastUpperBoundClosureType/A.groovy").element;
assertInstanceOf(ref.type, GrClosureType.class);
}
public void testJavaLangClassType() {
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("javaLangClassType/A.groovy").element;
assertEquals("java.lang.String", ref.type.canonicalText);
}
public void testGenericWildcard() {
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("genericWildcard/A.groovy").element;
assertEquals("A<Base>", ref.type.canonicalText);
}
public void testArrayLikeAccessWithIntSequence() {
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("arrayLikeAccessWithIntSequence/A.groovy").element;
assertEquals("java.util.List<java.lang.Integer>", ref.type.canonicalText);
}
public void testArrayAccess() {
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("arrayAccess/A.groovy");
assertEquals(JAVA_LANG_STRING, ref.type.canonicalText);
}
public void testReturnTypeByTailExpression() {
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("returnTypeByTailExpression/A.groovy");
assertEquals(JAVA_LANG_STRING, ref.type.canonicalText);
}
public void testParameterWithBuiltinType() {
GrReferenceExpression refExpr = (GrReferenceExpression)configureByFile("parameterWithBuiltinType/A.groovy");
assertEquals("java.lang.Integer", refExpr.type.canonicalText);
}
public void testRawTypeInReturnExpression() {
assertNotNull(resolve("A.groovy"));
}
public void testMethodCallInvokedOnArrayAccess() {
final GrReferenceExpression reference = (GrReferenceExpression)configureByFile("A.groovy");
assertNotNull(reference)
assertNotNull(reference.type)
assertEquals("java.lang.Integer", reference.type.canonicalText);
}
private void assertTypeEquals(String expected, String fileName) {
final PsiReference ref = configureByFile(getTestName(true) + "/" + fileName);
assertInstanceOf(ref, GrReferenceExpression.class);
final PsiType type = ((GrReferenceExpression)ref).type;
assertNotNull(type);
assertEquals(expected, type.canonicalText);
}
public void testTypeOfGroupBy() {
assertTypeEquals("java.util.Map<java.lang.Integer,java.util.List<java.lang.Integer>>", "A.groovy");
}
public void testConditionalExpressionWithNumericTypes() {
assertTypeEquals("java.lang.Number", "A.groovy");
}
public void testImplicitCallMethod() {
assertEquals("java.lang.String", ((GrExpression)configureByFile("A.groovy")).type.canonicalText);
}
public void testTupleWithNullInIt() {
assertTypeEquals("java.util.ArrayList", "A.groovy");
}
public void testImplicitlyReturnedMethodCall() {
assertTypeEquals("java.util.Map<BasicRange,java.util.Map<BasicRange,java.lang.Double>>", "A.groovy");
}
public void testInferWithClosureType() {
assertTypeEquals("java.util.Date", "A.groovy");
}
public void testPlusEquals1() {
assertTypeEquals("Test", "A.groovy");
}
public void testPlusEquals2() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testPlusEquals3() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testPlusEqualsClosure() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testGetAtClosure() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testPreferMethodOverloader() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testSafeInvocationInClassQualifier() {
assertTypeEquals("java.lang.Class", "SafeInvocationInClassQualifier.groovy");
}
public void testReturnTypeFromMethodClosure() {
assertTypeEquals("java.lang.String", "A.groovy");
}
public void testNoSOF() {
final PsiReference ref = configureByFile(getTestName(true) + "/A.groovy");
assertInstanceOf(ref, GrReferenceExpression.class);
final PsiType type = ((GrReferenceExpression)ref).type;
assertTrue(true); //test just should not fail with SOF exception
}
public void testTraditionalForVar() {
assertTypeEquals(JAVA_LANG_INTEGER, "A.groovy");
}
public void testIncMethod() {
assertTypeEquals(JAVA_LANG_INTEGER, "A.groovy");
}
public void testDGMFind() {
assertTypeEquals("java.io.File", "a.groovy");
}
public void testMultiTypeParameter() {
assertTypeEquals("X | Y", "a.groovy");
}
public void testTypeArgsInAccessor() {
assertTypeEquals("Foo<java.lang.String>", "a.groovy");
}
public void testSingleParameterInStringInjection() {
assertTypeEquals("java.io.StringWriter", "a.groovy");
}
void testIndexPropertyPlusAssigned() {
GroovyFile file = myFixture.configureByText('a.groovy', '''
class X {
def putAt(String s, X x){new Date()}
def getAt(String s) {new X()}
def plus(int i) {this}
}
map = new X()
map['i'] += 2
''') as GroovyFile
GrAssignmentExpression assignment = file.topStatements[2] as GrAssignmentExpression
assertType("X", assignment.type)
}
void testAllTypeParamsAreSubstituted() {
assertTypeEquals('java.util.Map', 'a.groovy')
}
void testDiamond() {
GroovyFile file = myFixture.configureByText('a.groovy', '''
List<String> list = new ArrayList<>()
List<Integer> l2
(list, l2) = [new ArrayList<>(), new ArrayList<>()]
''') as GroovyFile
def statements = file.topStatements
assertEquals('java.util.ArrayList<java.lang.String>', (statements[0] as GrVariableDeclaration).variables[0].initializerGroovy.type.canonicalText)
assertEquals('java.util.ArrayList<java.lang.String>', ((statements[2] as GrAssignmentExpression).RValue as GrListOrMap).initializers[0].type.canonicalText)
assertEquals('java.util.ArrayList<java.lang.Integer>', ((statements[2] as GrAssignmentExpression).RValue as GrListOrMap).initializers[1].type.canonicalText)
}
void testWildCardsNormalized() {
assertTypeEquals(Object.canonicalName, 'a.groovy')
}
void testIndexPropertyInLHS() {
assertTypeEquals("java.util.Map", 'a.groovy')
}
void testEmptyMapTypeArgs() {
myFixture.configureByText('a.groovy', '''
class X<A, B> implements Map<A, B> {}
X<String, Integer> x = [:]
''')
def type = ((myFixture.file as GroovyFile).statements[0] as GrVariableDeclaration).variables[0].initializerGroovy.type
assertEquals("java.util.Map<java.lang.String,java.lang.Integer>", type.canonicalText)
}
void testRawCollectionsInCasts() {
doTest('''\
String[] a = ["a"]
def b = a as ArrayList
def cc = b[0]
print c<caret>c''', String.canonicalName)
}
void testFind() {
doTest('''\
def arr = ['1', '2', '3'] as String[]
def found = arr.find({it=='1'})
print fou<caret>nd''', String.canonicalName)
}
void testFindAll() {
doTest('''\
def arr = ['1', '2', '3']
def found = arr.findAll({it==1})
print fou<caret>nd''', 'java.util.ArrayList<java.lang.String>')
}
void testFindAllForArray() {
doTest('''\
def arr = ['1', '2', '3'] as String[]
def found = arr.findAll({it==1})
print fou<caret>nd''', 'java.util.ArrayList<java.lang.String>')
}
void testFindAllForSet() {
myFixture.addClass('''\
package java.util;
class HashSet<T> implements Set<T> {} ''')
doTest('''\
def arr = ['1', '2', '3'] as Set<String>
def found = arr.findAll({it==1})
print fou<caret>nd''', 'java.util.HashSet<java.lang.String>')
}
void testInferArgumentTypeFromMethod1() {
doTest('''\
def bar(String s) {}
def foo(Integer a) {
while(true) {
bar(a)
<caret>a.substring(2)
}
}
''', '[java.lang.Integer,java.lang.String]')
}
void testInferArgumentTypeFromMethod2() {
doTest('''\
def bar(String s) {}
def foo(Integer a) {
bar(a)
<caret>a.substring(2)
}
''', '[java.lang.Integer,java.lang.String]')
}
void testInferArgumentTypeFromMethod3() {
doTest('''\
def bar(String s) {}
def foo(Integer a) {
bar(a)
print a
<caret>a.substring(2)
}
''', '[java.lang.Integer,java.lang.String]')
}
void testInferArgumentTypeFromMethod4() {
doTest('''\
def bar(String s) {}
def foo(Integer a) {
while(true) {
bar(a)
print a
<caret>a.substring(2)
}
}
''', '[java.lang.Integer,java.lang.String]')
}
void testEmptyListOrListWithGenerics() {
doTest('''\
def list = cond ? [1, 2, 3] : []
print lis<caret>t
''', "$JAVA_UTIL_LIST<$JAVA_LANG_INTEGER>")
}
void testEmptyListOrListWithGenerics2() {
doTest('''\
def List<Integer> foo(){}
def list = cond ? foo() : []
print lis<caret>t
''', "$JAVA_UTIL_LIST<$JAVA_LANG_INTEGER>")
}
void testEmptyMapOrMapWithGenerics() {
doTest('''\
def map = cond ? [1:'a', 2:'a', 3:'a'] : [:]
print ma<caret>p
''', "$JAVA_UTIL_MAP<$JAVA_LANG_STRING, $JAVA_LANG_STRING>")
}
void testEmptyMapOrMapWithGenerics2() {
doTest('''\
def Map<String, String> foo(){}
def map = cond ? foo() : [:]
print ma<caret>p
''', "$JAVA_UTIL_MAP<$JAVA_LANG_STRING,$JAVA_LANG_STRING>")
}
void testSpread1() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
[new A()].stri<caret>ng
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_STRING>")
}
void testSpread2() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
class Cat {
static getFoo(String b) {2}
}
use(Cat) {
[new A()].string.fo<caret>o
}
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>")
}
void testSpread3() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
[[new A()]].stri<caret>ng
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_STRING>>")
}
void testSpread4() {
myFixture.addClass('''\
class A {
String getString() {return "a";}
}''')
doTest('''\
class Cat {
static getFoo(String b) {2}
}
use(Cat){
[[new A()]].string.fo<caret>o
}
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>>")
}
void testInstanceOfInferring1() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String || oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring2() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String || o<caret>o != null
oo
}
''', null)
}
void testInstanceOfInferring3() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String && o<caret>o != null
oo
}
''', String.canonicalName)
}
void testInstanceOfInferring4() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String && oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring5() {
doTest('''\
def foo(def oo) {
if (oo instanceof String && oo instanceof CharSequence) {
oo
}
else {
o<caret>o
}
}
''', null)
}
void testInstanceOfInferring6() {
doTest('''\
def foo(bar) {
if (!(bar instanceof String) && bar instanceof Runnable) {
ba<caret>r
}
}''', 'java.lang.Runnable')
}
void testInString() {
doTest '''\
def foo(ii) {
if (ii in String)
print i<caret>i
}''', 'java.lang.String'
}
void testIndexProperty() {
doTest('''\
private void getCommonAncestor() {
def c1 = [new File('a')]
for (int i = 0; i < 2; i++) {
if (c1[i] != null) break
def cur = c1[i]
print cu<caret>r
}
}
''', 'java.io.File')
}
void testWildcardClosureParam() {
doTest('''\
class Tx {
def methodOfT() {}
}
def method(List<? extends Tx> t) {
t.collect { print i<caret>t }
}
''', 'Tx')
}
void testAssert() {
doTest('''\
def foo(def var) {
assert var instanceof String
va<caret>r.isEmpty()
}
''', 'java.lang.String')
}
void testUnresolvedSpread() {
doTest('''\
def xxx = abc*.name
print xx<caret>x''', 'java.util.List')
}
void testThisInCategoryClass() {
doTest('''\
class Cat {}
@groovy.lang.Category(Cat)
class Any {
void foo() {
print th<caret>is
}
}
''', 'Cat')
}
void testNormalizeTypeFromMap() {
doTest('''\
def pp = new HashMap<?, ?>().a
print p<caret>p
''', 'java.lang.Object')
}
void testUnary() {
doExprTest('~/abc/', 'java.util.regex.Pattern')
}
void testUnary2() {
doExprTest('-/abc/', null)
}
void testUnary3() {
doExprTest('''
class A {
def bitwiseNegate() {'abc'}
}
~new A()
''', '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')
}
void testMultiply2() {
doExprTest('2f*2f', 'java.lang.Double')
}
void testMultiply3() {
doExprTest('2d*2d', 'java.lang.Double')
}
void testMultiply4() {
doExprTest('2.4*2', 'java.math.BigDecimal')
}
void testMultiply5() {
doExprTest('((byte)2)*((byte)2)', 'java.lang.Integer')
}
void testMultiply6() {
doExprTest('"abc"*"cde"', 'java.lang.String') //expected number as a right operand
}
void testMultiply7() {
doExprTest('''
class A {
def multiply(A a) {new B()}
}
class B{}
new A()*new A()
''', 'B')
}
void testMultiply8() {
doExprTest('''
class A { }
new A()*new A()
''', null)
}
void testDiv1() {
doExprTest('1/2', 'java.math.BigDecimal')
}
void testDiv2() {
doExprTest('1/2.4', 'java.math.BigDecimal')
}
void testDiv3() {
doExprTest('1d/2', 'java.lang.Double')
}
void testDiv4() {
doExprTest('1f/2', 'java.lang.Double')
}
void testDiv5() {
doExprTest('1f/2.4', 'java.lang.Double')
}
void testRecursionWithMaps() {
doTest('''
def foo(Map map) {
while(true)
ma<caret>p = [a:map]
}
''', 'java.util.Map<java.lang.String, java.util.Map>')
}
void testRecursionWithLists() {
doTest('''
def foo(List list) {
while(true)
lis<caret>t = [list]
}
''', 'java.util.ArrayList<java.util.List>')
}
}