mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-110455 Groovy 2.2: Closure is assignable to SAM classes
This commit is contained in:
@@ -150,6 +150,7 @@
|
||||
<typeConverter implementation="org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrStringTypeConverter"/>
|
||||
<typeConverter implementation="org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrBooleanTypeConverter"/>
|
||||
<typeConverter implementation="org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrGenericTypeConverter"/>
|
||||
<typeConverter implementation="org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureToSamConverter"/>
|
||||
|
||||
<typeConverter implementation="org.jetbrains.plugins.groovy.gpp.GppTypeConverter"/>
|
||||
<expectedTypesContributor implementation="org.jetbrains.plugins.groovy.gpp.GppExpectedTypesContributor"/>
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.psi.typeEnhancers;
|
||||
|
||||
import com.intellij.codeInsight.generation.OverrideImplementExploreUtil;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.util.LightCacheKey;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public class ClosureToSamConverter extends GrTypeConverter {
|
||||
private static final LightCacheKey<Ref<MethodSignature>> SAM_SIGNATURE_LIGHT_CACHE_KEY = LightCacheKey.createByJavaModificationCount();
|
||||
|
||||
@Override
|
||||
public boolean isAllowedInMethodCall() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isConvertible(@NotNull PsiType ltype, @NotNull PsiType rtype, @NotNull final GroovyPsiElement context) {
|
||||
if (rtype instanceof GrClosureType && ltype instanceof PsiClassType && GroovyConfigUtils.getInstance().isVersionAtLeast(context, GroovyConfigUtils.GROOVY2_2)) {
|
||||
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)ltype).resolveGenerics();
|
||||
final PsiClass resolved = resolveResult.getElement();
|
||||
if (resolved != null) {
|
||||
final MethodSignature signature = findSingleAbstractMethodClass(resolved, resolveResult.getSubstitutor());
|
||||
if (signature != null) {
|
||||
|
||||
final PsiType[] samParameterTypes = signature.getParameterTypes();
|
||||
|
||||
GrSignature closureSignature = ((GrClosureType)rtype).getSignature();
|
||||
if (GrClosureSignatureUtil.isSignatureApplicable(closureSignature, samParameterTypes, context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MethodSignature findSingleAbstractMethodClass(@NotNull PsiClass aClass,
|
||||
@NotNull PsiSubstitutor substitutor) {
|
||||
MethodSignature signature;
|
||||
Ref<MethodSignature> cached = SAM_SIGNATURE_LIGHT_CACHE_KEY.getCachedValue(aClass);
|
||||
if (cached != null) {
|
||||
signature = cached.get();
|
||||
}
|
||||
else {
|
||||
cached = Ref.create(doFindSingleAbstractMethodClass(aClass));
|
||||
signature = SAM_SIGNATURE_LIGHT_CACHE_KEY.putCachedValue(aClass, cached).get();
|
||||
}
|
||||
|
||||
return signature != null ? substitute(signature, substitutor): null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MethodSignature doFindSingleAbstractMethodClass(@NotNull PsiClass aClass) {
|
||||
Collection<MethodSignature> toImplement = OverrideImplementExploreUtil.getMethodSignaturesToImplement(aClass);
|
||||
if (toImplement.size() > 1) return null;
|
||||
|
||||
MethodSignature abstractSignature = toImplement.isEmpty() ? null : toImplement.iterator().next();
|
||||
for (PsiMethod method : aClass.getMethods()) {
|
||||
if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
if (abstractSignature != null) return null;
|
||||
abstractSignature = method.getSignature(PsiSubstitutor.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
return abstractSignature;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MethodSignature substitute(@NotNull MethodSignature signature, @NotNull PsiSubstitutor substitutor) {
|
||||
return MethodSignatureUtil.createMethodSignature(signature.getName(), signature.getParameterTypes(), PsiTypeParameter.EMPTY_ARRAY, substitutor, false);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.highlighting
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
|
||||
import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public class GrAssignabilityClosureToSamTest extends GrHighlightingTestBase {
|
||||
InspectionProfileEntry[] getCustomInspections() { [new GroovyAssignabilityCheckInspection()] }
|
||||
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() { GroovyLightProjectDescriptor.GROOVY_2_2 }
|
||||
|
||||
void testAssignability() {
|
||||
testHighlighting('''\
|
||||
interface A {
|
||||
def foo()
|
||||
}
|
||||
interface B {}
|
||||
|
||||
A a = {print 1}
|
||||
A a1 = {->print 1}
|
||||
A <warning>a2</warning> = {String s->print 1}
|
||||
B <warning>b</warning> = {print 2}
|
||||
''')
|
||||
}
|
||||
|
||||
void testAmbiguous() {
|
||||
testHighlighting('''\
|
||||
interface SAM1 { def foo(String s)}
|
||||
interface SAM2 { def bar(Integer i)}
|
||||
|
||||
def method(x, SAM1 s1){s1.foo<weak_warning>(x)</weak_warning>}
|
||||
def method(x, SAM2 s2){s2.bar<weak_warning>(x)</weak_warning>}
|
||||
|
||||
method <warning>(1)</warning> {it} // fails because SAM1 and SAM2 are seen as equal
|
||||
method <warning>("1")</warning> {it} // fails because SAM1 and SAM2 are seen as equal
|
||||
|
||||
''')
|
||||
}
|
||||
|
||||
void testGenerics() {
|
||||
testHighlighting('''
|
||||
interface A<T> {
|
||||
def foo(T t)
|
||||
}
|
||||
|
||||
A<String> a1 = {print 1}
|
||||
A<String> a2 = {String s -> print 1}
|
||||
A<String> <warning>a3</warning> = {int s -> print 1}
|
||||
A <warning>a4</warning> = {int s -> print 1}
|
||||
A a5 = { print 1}
|
||||
A a6 = {x -> print 1}
|
||||
''')
|
||||
}
|
||||
|
||||
void testGenerics2() {
|
||||
testHighlighting('''
|
||||
interface A<T> {
|
||||
def foo(T t)
|
||||
}
|
||||
|
||||
interface B extends A<String> {}
|
||||
|
||||
B b1 = {print 1}
|
||||
B b2 = {String s -> print 1}
|
||||
B <warning>b3</warning> = {int s -> print 1}
|
||||
''')
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -774,4 +774,13 @@ new A.Inner(new A(), new A()) //correct
|
||||
''')
|
||||
}
|
||||
|
||||
void testClosureIsNotAssignableToSAMInGroovy2_1() {
|
||||
testHighlighting('''\
|
||||
interface X {
|
||||
def foo()
|
||||
}
|
||||
|
||||
X <warning>x</warining> = {print 2}
|
||||
''')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user