| /* |
| * 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 |
| |
| import com.intellij.codeInsight.lookup.LookupManager |
| import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection |
| import com.intellij.openapi.module.Module |
| import com.intellij.openapi.roots.ContentEntry |
| import com.intellij.openapi.roots.ModifiableRootModel |
| import com.intellij.openapi.roots.OrderRootType |
| import com.intellij.openapi.roots.libraries.Library |
| import com.intellij.openapi.vfs.JarFileSystem |
| import com.intellij.psi.* |
| import com.intellij.testFramework.LightProjectDescriptor |
| import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor |
| import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase |
| import junit.framework.ComparisonFailure |
| import org.jetbrains.annotations.NotNull |
| import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection |
| import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection |
| import org.jetbrains.plugins.groovy.codeInspection.unassignedVariable.UnassignedVariableAccessInspection |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod |
| import org.jetbrains.plugins.groovy.util.TestUtils |
| /** |
| * @author peter |
| */ |
| class GppFunctionalTest extends LightCodeInsightFixtureTestCase { |
| |
| @NotNull |
| @Override |
| protected LightProjectDescriptor getProjectDescriptor() { |
| return GppProjectDescriptor.instance; |
| } |
| |
| protected void setUp() { |
| super.setUp() |
| } |
| |
| public void testCastListToIterable() throws Exception { |
| myFixture.addClass("class X extends java.util.ArrayList<Integer> {}") |
| testAssignability """ |
| X ints = [239, 4.2d] |
| """ |
| } |
| |
| public void testCastListToAnything() throws Exception { |
| testAssignability """ |
| File f1 = ['path'] |
| File f2 = <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '(java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Integer)'">['path', 2, true, 42]</warning> |
| """ |
| } |
| |
| public void testCastMapToAnotherMap() throws Exception { |
| myFixture.addClass """ |
| public class Y extends java.util.HashMap<String, String> { |
| public Y(int initialCapacity) { |
| super(initialCapacity); |
| } |
| } |
| """ |
| |
| testAssignability """ |
| HashMap<String, File> m1 = ['a':['b']] |
| Y y = <warning descr="Constructor 'Y' in 'Y' cannot be applied to '(['a':java.lang.String])'">[a:'b']</warning> |
| """ |
| } |
| |
| public void testAnonymousClass() throws Exception { |
| myFixture.enableInspections new GroovyAssignabilityCheckInspection() |
| testAssignability """ |
| def x = new Object() { |
| def foo() { |
| HashMap<String, File> m1 = ['a':['b']] |
| HashMap<String, File> <warning descr="Cannot assign 'File' to 'HashMap<String, File>'">m2</warning> = new File('aaa') |
| } |
| } |
| """ |
| } |
| |
| public void testCastMapToObject() throws Exception { |
| myFixture.addClass("class Foo { String name; void foo() {} }") |
| testAssignability """ |
| Foo f = [name: 'aaa', foo: { println 'hi' }, anotherProperty: 42 ] |
| """ |
| } |
| |
| void testAssignability(String text) { |
| myFixture.enableInspections new GroovyAssignabilityCheckInspection() |
| PsiFile file = configureGppScript(text) |
| myFixture.testHighlighting(true, false, false, file.virtualFile) |
| } |
| |
| private PsiFile configureScript(String text) { |
| return myFixture.configureByText("a.groovy", text) |
| } |
| private PsiFile configureGppScript(String text) { |
| return myFixture.configureByText("a.gpp", text) |
| } |
| |
| public void testDeclaredVariableTypeIsMoreImportantThanTheInitializerOne() throws Exception { |
| configureScript(""" |
| File f = ['path'] |
| f.mk<caret> |
| """) |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "mkdir", "mkdirs" |
| } |
| |
| public void testDeclaredVariableTypeIsMoreImportantThanTheInitializerOne2() throws Exception { |
| myFixture.addClass """ |
| public class Some { |
| public int prop |
| |
| public void f_foo() {} |
| public void f_bar() {} |
| } |
| """ |
| |
| configureScript(""" |
| Some s = [prop: 239] |
| s.f_<caret> |
| """) |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "f_foo", "f_bar" |
| } |
| |
| public void testResolveMethod() throws Exception { |
| myFixture.configureByText("a.groovy", """ |
| def foo(File f) {} |
| @Typed def bar() { |
| fo<caret>o(['path']) |
| } |
| """) |
| def reference = findReference() |
| def target = reference.resolve() |
| assertEquals "foo", ((GrMethod)target).name |
| } |
| |
| private PsiReference findReference() { |
| return myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset) |
| } |
| |
| public void testOverloadingWithConversion() throws Exception { |
| myFixture.configureByText("a.groovy", """ |
| def foo(List l) {} |
| def foo(File f) {} |
| @Typed def bar() { |
| fo<caret>o(['path']) |
| } |
| """) |
| def reference = findReference() |
| def target = reference.resolve() |
| assertNotNull target |
| assert target.text.contains("List l") |
| } |
| |
| public void testWrongProperty() throws Exception { |
| myFixture.configureByText 'a.gpp', ''' |
| class ClassA { |
| def prop = 2 |
| def bar() { |
| def t = new Object() |
| t.pr<caret>op |
| } |
| }''' |
| assert !findReference().resolve() |
| } |
| |
| public void testCastClosureToOneMethodClass() throws Exception { |
| myFixture.addClass """ |
| public abstract class Foo { |
| public abstract void foo(String s); |
| public abstract void bar(String s); |
| } |
| public interface Action { |
| void act(); |
| } |
| """ |
| |
| testAssignability """ |
| Foo <warning descr="Cannot assign 'Closure' to 'Foo'">f</warning> = { println it } |
| Function1<String, Object> f1 = { println it } |
| Function1<String, Object> f2 = { x=42 -> println x } |
| Function1<String, Object> <warning descr="Cannot assign 'Closure' to 'Function1<String, Object>'">f3</warning> = { int x -> println x } |
| Runnable r = { println it } |
| Action a = { println it } |
| Action a1 = { a2 = 2 -> println a2 } |
| """ |
| } |
| |
| public void testClosureParameterTypesInAssignment() throws Exception { |
| configureScript "Function1<String, Object> f = { it.subs<caret> }" |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" |
| } |
| |
| public void testClosureParameterTypesInMethodInvocation() throws Exception { |
| myFixture.configureByText "a.groovy", """ |
| def foo(int a = 1, Function1<String, Object> f) {} |
| def foo(String s) {} |
| def foo(Function2<Integer, String, Object> f) {} |
| |
| @Typed def bar() { |
| foo { it.subsREF } |
| foo(1, { it.subsREF }) |
| foo 1, { it.subsREF } |
| foo(1) { it.subsREF } |
| foo { a -> a.subsREF } |
| foo { a, int b=2 -> a.subsREF } |
| foo { a, b -> b.subsREF } |
| } |
| """ |
| def text = myFixture.file.text |
| def pos = 0 |
| while (true) { |
| pos = text.indexOf("REF", pos+1) |
| if (pos < 0) { |
| break |
| } |
| myFixture.editor.caretModel.moveToOffset pos |
| myFixture.completeBasic() |
| try { |
| assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" |
| } |
| catch (ComparisonFailure ex) { |
| println "at: " + text[0..<pos] + "<caret>" + text[pos..<text.size()] |
| throw ex |
| } |
| LookupManager.getInstance(project).hideActiveLookup() |
| } |
| } |
| |
| public void testReturnTypeOneMethodInterface() throws Exception { |
| myFixture.configureByText "a.groovy", """ |
| @Typed Function1<String, Integer> bar() { |
| { it.subs<caret> } |
| } |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" |
| } |
| |
| public void testClosureInMapInstantiation() throws Exception { |
| myFixture.configureByText "a.groovy", """ |
| class Foo<T> { |
| int foo(T a) {} |
| } |
| |
| @Typed Foo<String> bar() { |
| return [foo: { it.subs<caret> }] |
| } |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" |
| } |
| |
| public void testClosureInMapInstantiationBoxPrimitives() throws Exception { |
| myFixture.configureByText "a.groovy", """ |
| class Foo { |
| int foo(int a) {} |
| } |
| |
| @Typed Foo bar() { |
| return [foo: { it.intV<caret>V }] |
| } |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "intValue" |
| } |
| |
| public void testClosureInListInstantiation() throws Exception { |
| myFixture.configureByText "a.groovy", """ |
| class Foo { |
| def Foo(int a, Function1<String, Integer> f) {} |
| } |
| |
| @Typed Foo foo() { |
| [239, { s -> s.subs<caret> }] |
| } |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.lookupElementStrings, "subSequence", "substring", "substring" |
| } |
| |
| public void testResolveToStdLibWithArrayQualifier() throws Exception { |
| configureGppScript """ |
| Integer[] a = [] |
| a.fol<caret>dLeft(2, { a, b -> a+b }) |
| """ |
| PsiMethod method = resolveReference().navigationElement as PsiMethod |
| assertEquals "foldLeft", method.name |
| assertEquals "groovypp.util.Iterations", method.containingClass.qualifiedName |
| } |
| |
| private PsiElement resolveReference() { |
| return findReference().resolve() |
| } |
| |
| public void testResolveToSuperMethodClosureSyntax() { |
| configureScript """ |
| abstract class Super implements Runnable { |
| def method(int bar) {} |
| } |
| |
| Super s = { <caret>method(2) } as Super |
| """ |
| assert resolveReference() instanceof GrMethod |
| } |
| |
| public void testMethodTypeParameterInference() throws Exception { |
| configureScript """ |
| @Typed package aaa |
| |
| java.util.concurrent.atomic.AtomicReference<Integer> r = [2] |
| r.apply { it.intV<caret>i } |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.getLookupElementStrings(), "intValue" |
| } |
| |
| public void testMethodTypeParameterInference2() throws Exception { |
| configureScript """ |
| @Typed package aaa |
| |
| java.util.concurrent.atomic.AtomicReference<Integer> r = [2] |
| r.apply { it.intV<caret>i } {} |
| """ |
| myFixture.completeBasic() |
| assertSameElements myFixture.getLookupElementStrings(), "intValue" |
| } |
| |
| public void testGotoSuperMethodFromMapLiterals() throws Exception { |
| PsiClass point = myFixture.addClass(""" |
| class Point { |
| Point() {} |
| Point(int y) {} |
| int y; |
| void setX(int x) {} |
| void move(int x, int y) {} |
| void move(int y) {} |
| }""") |
| |
| configureScript "Point p = [<caret>y:2]" |
| assertEquals point.findFieldByName("y", false), resolveReference() |
| |
| configureScript "Point p = [<caret>x:2]" |
| assertEquals point.findMethodsByName("setX", false)[0], resolveReference() |
| |
| configureScript "Point p = [mo<caret>ve: { x, y -> z }]" |
| assertEquals point.findMethodsByName("move", false)[0], resolveReference() |
| |
| configureScript "Point p = [mo<caret>ve: ]" |
| def resolveResults = multiResolveReference() |
| assertSameElements resolveResults.collect { it.element }, point.findMethodsByName("move", false) |
| } |
| |
| ResolveResult[] multiResolveReference() { |
| final PsiReference ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset) |
| assertNotNull(ref) |
| assertInstanceOf(ref, PsiPolyVariantReference) |
| return ((PsiPolyVariantReference)ref).multiResolve(true) |
| } |
| |
| public void testGotoSuperConstructorFromMapLiterals() throws Exception { |
| PsiClass point = myFixture.addClass(""" |
| class Point { |
| Point() {} |
| Point(int y) {} |
| }""") |
| |
| configureGppScript "Point p = [su<caret>per: 2]" |
| assertEquals point.constructors[1], resolveReference() |
| |
| configureGppScript "Point p = [su<caret>per: [2]]" |
| assertEquals point.constructors[1], resolveReference() |
| |
| configureGppScript "Point p = ['su<caret>per': []]" |
| assertEquals point.constructors[0], resolveReference() |
| |
| configureGppScript "Point p = ['su<caret>per': 'a']" |
| assertEquals 1, multiResolveReference().size() |
| } |
| |
| public void testGotoClassFromLiteralOnsetsWhenNoConstructorsPresent() throws Exception { |
| PsiClass point = myFixture.addClass(""" class Point { }""") |
| configureGppScript "Point p = <caret>[super: 2]" |
| assertEquals point, resolveReference() |
| |
| configureGppScript "Point p = <caret>[]" |
| assertEquals point, resolveReference() |
| } |
| |
| public void testNoGotoObjectFromLiteral() throws Exception { |
| myFixture.addClass(""" class Point { }""") |
| |
| configureGppScript "def p = <caret>[]" |
| assertNull findReference().resolve() |
| } |
| |
| public void testHighlightInapplicableLiteralConstructor() throws Exception { |
| myFixture.addClass(""" |
| class Point { |
| Point() {} |
| }""") |
| |
| configureGppScript """ |
| def foo(Point p) {} |
| Point p = [:] |
| Point p2 = [super:warning descr="Cannot find constructor of 'Point'">[4, 2]</warning>] |
| foo(<warning descr="Cannot find constructor of 'Point'">[4, 2]</warning>) |
| """ |
| } |
| |
| public void testResolveTraitMethod() throws Exception { |
| configureScript """ |
| @Trait |
| class Some { |
| public void doSmth() { println "hello" } |
| } |
| Some s |
| s.do<caret>Smth() |
| """ |
| assertEquals "doSmth", ((PsiMethod) findReference().resolve()).name |
| } |
| |
| public void testBaseConstructorCallInMapLiteras() throws Exception { |
| configureScript """ |
| @Typed File foo() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '(['super':[java.lang.String]])'">['super':['a']]</warning> } |
| @Typed File goo() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '([:])'">[:]</warning> } |
| File bar() { <warning descr="Constructor 'File' in 'java.io.File' cannot be applied to '([:])'">[:]</warning> } |
| """ |
| myFixture.enableInspections new GroovyAssignabilityCheckInspection() |
| myFixture.checkHighlighting(true, false, false) |
| } |
| |
| public void testNestedLiteralConstructors() throws Exception { |
| configureGppScript """ |
| class Foo { |
| def Foo(Bar b) { } |
| } |
| |
| class Bar { |
| def Bar(int i) { } |
| } |
| |
| Foo x = <warning descr="Constructor 'Foo' in 'Foo' cannot be applied to '([java.lang.Integer])'">[[2]]</warning> |
| println x |
| """ |
| myFixture.enableInspections new GroovyAssignabilityCheckInspection() |
| myFixture.checkHighlighting(true, false, false) |
| } |
| |
| public void testNoReturnTypeInferenceInTypedContext() throws Exception { |
| configureGppScript """ |
| class Foo { |
| def foo() { "aaa" } |
| } |
| new Foo().foo().substr<caret>a |
| """ |
| assertEmpty myFixture.completeBasic() |
| } |
| |
| public void testDeclaredReturnTypeInTypedContext() throws Exception { |
| configureGppScript """ |
| class Foo { |
| String getFoo() { "aaa" } |
| } |
| new Foo().foo.substr<caret>a |
| """ |
| myFixture.completeBasic() |
| assertOrderedEquals myFixture.lookupElementStrings, "substring", "substring" |
| } |
| |
| public void testNonInitializedVariable() throws Exception { |
| configureScript """ |
| |
| @Typed |
| def foo() { |
| int a |
| return a |
| } |
| |
| def bar() { |
| int a |
| return <warning descr="Variable 'a' might not be assigned">a</warning> |
| }""" |
| myFixture.enableInspections new UnassignedVariableAccessInspection() |
| myFixture.checkHighlighting(true, false, false) |
| } |
| |
| public void testExternalizable() throws Exception { |
| configureScript ''' |
| @Typed class Foo implements Externalizable {} |
| <error descr="Method 'writeExternal' is not implemented">class Bar implements Externalizable</error> {} |
| ''' |
| myFixture.checkHighlighting(true, false, false) |
| } |
| |
| public void testUsedInterceptors() { |
| configureGppScript ''' |
| class Bar { |
| Object getUnresolvedProperty(String <warning descr="Parameter name is unused">name</warning>) {} |
| Object <warning descr="Method getUnresolvedProperty is unused">getUnresolvedProperty</warning>(int <warning descr="Parameter name is unused">name</warning>) {} |
| void setUnresolvedProperty(String <warning descr="Parameter name is unused">name</warning>, String <warning descr="Parameter value is unused">value</warning>) {} |
| int invokeUnresolvedMethod(String <warning descr="Parameter name is unused">name</warning>, String <warning descr="Parameter arg1 is unused">arg1</warning>, boolean <warning descr="Parameter arg2 is unused">arg2</warning>, Object... <warning descr="Parameter args is unused">args</warning>) {} |
| int invokeUnresolvedMethod(String <warning descr="Parameter name is unused">name</warning>, Object... <warning descr="Parameter args is unused">args</warning>) {} |
| int <warning descr="Method invokeUnresolvedMethod is unused">invokeUnresolvedMethod</warning>(Object... <warning descr="Parameter args is unused">args</warning>) {} |
| } |
| println new Bar().zzz |
| ''' |
| myFixture.enableInspections(new GroovyUnusedDeclarationInspection(), new UnusedDeclarationInspection()) |
| myFixture.checkHighlighting(true, false, false) |
| } |
| |
| } |
| |
| class GppProjectDescriptor extends DefaultLightProjectDescriptor { |
| public static final instance = new GppProjectDescriptor() |
| |
| @Override |
| public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { |
| final Library.ModifiableModel modifiableModel = model.moduleLibraryTable.createLibrary("GROOVY++").modifiableModel; |
| modifiableModel.addRoot(JarFileSystem.instance.refreshAndFindFileByPath(TestUtils.absoluteTestDataPath + "mockGroovypp/groovypp-0.9.0_1.8.2.jar!/"), OrderRootType.CLASSES) |
| modifiableModel.addRoot(JarFileSystem.instance.refreshAndFindFileByPath(TestUtils.mockGroovy1_7LibraryName + "!/"), OrderRootType.CLASSES); |
| modifiableModel.commit(); |
| } |
| } |