method vs field with closure initializer
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sat, 27 Feb 2010 12:48:32 +0000 (15:48 +0300)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sat, 27 Feb 2010 12:48:32 +0000 (15:48 +0300)
plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/ClosureSyntheticParameter.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/LightParameter.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.java
plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy [new file with mode: 0644]
plugins/groovy/testdata/resolve/method/methodVsField/A.groovy [new file with mode: 0644]

index 033a556a92160675a7e3602ac5d822df22bdbc81..fd9960457e280ad8f432c143d30f5154b9150ca9 100644 (file)
@@ -1148,29 +1148,47 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
   private static void checkMethodApplicability(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder) {
     final PsiElement element = methodResolveResult.getElement();
     if (!(element instanceof PsiMethod)) return;
+
     final PsiMethod method = (PsiMethod)element;
     PsiType[] argumentTypes = PsiUtil.getArgumentTypes(place, method.isConstructor(), true);
-    if (argumentTypes != null &&
-        !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(),
-                              methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) {
-      PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
-      if (elementToHighlight == null) {
-        elementToHighlight = place;
+    if ("call".equals(method.getName()) && place instanceof GrReferenceExpression) {
+      final GrExpression qualifierExpression = ((GrReferenceExpression)place).getQualifierExpression();
+      if (qualifierExpression != null) {
+        final PsiType type = qualifierExpression.getType();
+        if (type instanceof GrClosureType) {
+          if (!PsiUtil.isApplicable(argumentTypes, (GrClosureType)type, element.getManager())) {
+            highlightInapplicableMethodUsage(methodResolveResult, place, holder, method, argumentTypes);
+            return;
+          }
+        }
       }
+    }
+    if (argumentTypes != null &&
+             !PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(),
+                                   methodResolveResult.getCurrentFileResolveContext() instanceof GrMethodCallExpression)) {
+      highlightInapplicableMethodUsage(methodResolveResult, place, holder, method, argumentTypes);
+    }
+  }
 
-      final String typesString = buildArgTypesList(argumentTypes);
-      String message;
-      final PsiClass containingClass = method.getContainingClass();
-      if (containingClass != null) {
-        final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory()
-          .createType(containingClass, methodResolveResult.getSubstitutor());
-        message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString);
-      }
-      else {
-        message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString);
-      }
-      holder.createWarningAnnotation(elementToHighlight, message);
+  private static void highlightInapplicableMethodUsage(GroovyResolveResult methodResolveResult, PsiElement place, AnnotationHolder holder,
+                                                        PsiMethod method, PsiType[] argumentTypes) {
+    PsiElement elementToHighlight = PsiUtil.getArgumentsElement(place);
+    if (elementToHighlight == null) {
+      elementToHighlight = place;
+    }
+
+    final String typesString = buildArgTypesList(argumentTypes);
+    String message;
+    final PsiClass containingClass = method.getContainingClass();
+    if (containingClass != null) {
+      final PsiClassType containingType = JavaPsiFacade.getInstance(method.getProject()).getElementFactory()
+        .createType(containingClass, methodResolveResult.getSubstitutor());
+      message = GroovyBundle.message("cannot.apply.method1", method.getName(), containingType.getInternalCanonicalText(), typesString);
+    }
+    else {
+      message = GroovyBundle.message("cannot.apply.method.or.closure", method.getName(), typesString);
     }
+    holder.createWarningAnnotation(elementToHighlight, message);
   }
 
   public static boolean isDeclarationAssignment(GrReferenceExpression refExpr) {
index 6175091a67af0421e0aef69df45d87459256f704..fce06d1b78d4176d4a436cadcf0e4a129c2817c1 100644 (file)
@@ -407,7 +407,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
       return GroovyResolveResult.EMPTY_ARRAY;
     }
 
-    private void resolveImpl(GrReferenceExpressionImpl refExpr, ResolverProcessor processor) {
+    private static void resolveImpl(GrReferenceExpressionImpl refExpr, ResolverProcessor processor) {
       GrExpression qualifier = refExpr.getQualifierExpression();
       if (qualifier == null) {
         ResolveUtil.treeWalkUp(refExpr, processor, true);
index 605b4752ae92d663f737ca23f16faa12bda6fc88..58a23b0080e2133400c04279dcfcf6a0cde26f25 100644 (file)
@@ -26,6 +26,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableBase;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.params.GrParameterImpl;
 
@@ -93,4 +94,14 @@ public class ClosureSyntheticParameter extends LightParameter implements Navigat
   public PsiElement getContext() {
     return myClosure;
   }
+
+  @Override
+  public boolean isOptional() {
+    return true;
+  }
+
+  @Override
+  public GrExpression getDefaultInitializer() {
+    return GroovyPsiElementFactory.getInstance(getProject()).createExpressionFromText("null");
+  }
 }
index ff7033ffdead635fe987a2bc401a85f3f978cd63..b1877e64f89eb84d6f5ae2bfa311ddf96f365732 100644 (file)
@@ -18,12 +18,18 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.synthetic;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.*;
 import com.intellij.psi.impl.light.LightVariableBase;
+import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
+import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
 
 /**
  * @author ven
  */
-public class LightParameter extends LightVariableBase implements PsiParameter {
+public class LightParameter extends LightVariableBase implements GrParameter {
   public static final LightParameter[] EMPTY_ARRAY = new LightParameter[0];
   private final String myName;
 
@@ -34,7 +40,7 @@ public class LightParameter extends LightVariableBase implements PsiParameter {
 
   public void accept(@NotNull PsiElementVisitor visitor) {
     if (visitor instanceof JavaElementVisitor) {
-      ((JavaElementVisitor) visitor).visitParameter(this);
+      ((JavaElementVisitor)visitor).visitParameter(this);
     }
   }
 
@@ -55,4 +61,43 @@ public class LightParameter extends LightVariableBase implements PsiParameter {
   public String getName() {
     return StringUtil.notNullize(myName);
   }
+
+  public GrTypeElement getTypeElementGroovy() {
+    return null;
+  }
+
+  public GrExpression getDefaultInitializer() {
+    return null;
+  }
+
+  public boolean isOptional() {
+    return false;
+  }
+
+  public GrExpression getInitializerGroovy() {
+    return null;
+  }
+
+  public void setType(@Nullable PsiType type) throws IncorrectOperationException {
+  }
+
+  @NotNull
+  public PsiElement getNameIdentifierGroovy() {
+    return myNameIdentifier;
+  }
+
+  public PsiType getTypeGroovy() {
+    return getType();
+  }
+
+  public PsiType getDeclaredType() {
+    return getType();
+  }
+
+  public void accept(GroovyElementVisitor visitor) {
+  }
+
+  public void acceptChildren(GroovyElementVisitor visitor) {
+
+  }
 }
index cf905db7672ff551180b8b5bedcadc9b78707b5e..b24d3fef81d5ef9a9ae654e50c95ea16557fe4a5 100644 (file)
@@ -34,6 +34,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssign
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
@@ -83,16 +84,19 @@ public class MethodResolverProcessor extends ResolverProcessor {
       }
 
       return true;
-    } else if (element instanceof PsiVariable) {
-      if (element instanceof GrField && ((GrField) element).isProperty() ||
-          isClosure((PsiVariable) element)) {
-        return super.execute(element, state);
-      } else {
-        myInapplicableCandidates.add(new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element), isStaticsOK((PsiVariable)element)));
+    }
+    else if (element instanceof PsiVariable) {
+      if (isApplicableClosure((PsiVariable)element)) {
+        myCandidates.add(new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element),
+                                                     isStaticsOK((PsiVariable)element)));
+      }
+      else {
+        myInapplicableCandidates.add(
+          new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element),
+                                      isStaticsOK((PsiVariable)element)));
       }
     }
 
-
     return true;
   }
 
@@ -126,10 +130,14 @@ public class MethodResolverProcessor extends ResolverProcessor {
     return substitutor;
   }
 
-  private static boolean isClosure(PsiVariable variable) {
+  private boolean isApplicableClosure(PsiVariable variable) {
     if (variable instanceof GrVariable) {
-      final PsiType type = ((GrVariable) variable).getTypeGroovy();
-      return type != null && type.equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE);
+      final PsiType type = ((GrVariable)variable).getTypeGroovy();
+      if (type == null) return false;
+      if (type instanceof GrClosureType) {
+        return PsiUtil.isApplicable(myArgumentTypes, (GrClosureType)type, variable.getManager());
+      }
+      if (type.equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE)) return true;
     }
     return variable.getType().equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE);
   }
@@ -317,10 +325,6 @@ public class MethodResolverProcessor extends ResolverProcessor {
     return myArgumentTypes;
   }
 
-  public void setArgumentTypes(@Nullable PsiType[] argumentTypes) {
-    myArgumentTypes = argumentTypes;
-  }
-
   @Override
   public void handleEvent(Event event, Object associated) {
     super.handleEvent(event, associated);
index 7bd8b61e07144ca1839afad8a3c460a543e1e7c9..4d65aedae35e0908d927daee73de2c031b80c305 100644 (file)
@@ -194,6 +194,7 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase {
 
   public void testMethodCallWithDefaultParameters() throws Exception {doTest();}
   public void testClosureWithDefaultParameters() throws Exception {doTest();}
+  public void testClosureCallMethodWithInapplicableArguments() throws Exception {doTest();}
 
   public void testOverlyLongMethodInspection() throws Exception {
     doTest(new GroovyOverlyLongMethodInspection());
index 146af08a9ae441f83a1645c953fe3b8bb7881f86..2303bf5f7d4cf86abbfb471a8518b7b9343e226c 100644 (file)
@@ -546,4 +546,10 @@ public class ResolveMethodTest extends GroovyResolveTestCase {
     final PsiElement resolved = ref.resolve();
     assertInstanceOf(resolved, PsiMethod.class);
   }
+
+  public void testMethodVsField() throws Exception {
+    final PsiReference ref = configureByFile("methodVsField/A.groovy");
+    final PsiElement element = ref.resolve();
+    assertInstanceOf(element, PsiMethod.class);
+  }
 }
diff --git a/plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy b/plugins/groovy/testdata/highlighting/ClosureCallMethodWithInapplicableArguments.groovy
new file mode 100644 (file)
index 0000000..58275e3
--- /dev/null
@@ -0,0 +1,7 @@
+def foo={x, y->}
+
+print foo.call<warning descr="'call' in 'groovy.lang.Closure' cannot be applied to '(java.lang.Integer)'">(1)</warning>
+
+def bar={3}
+print bar.call()
+print bar.call(3)
\ No newline at end of file
diff --git a/plugins/groovy/testdata/resolve/method/methodVsField/A.groovy b/plugins/groovy/testdata/resolve/method/methodVsField/A.groovy
new file mode 100644 (file)
index 0000000..aaed6df
--- /dev/null
@@ -0,0 +1,17 @@
+class Foo {
+
+
+  def bar = { 2}
+}
+
+class Bar extends Foo {
+  def bar(def it) { 3 }
+
+  public static void main(String[] args) {
+    def f = new Bar()
+
+    
+    println f.ba<ref>r(3)
+  }
+
+}