smart completion for unary operations
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Fri, 12 Feb 2010 08:58:39 +0000 (11:58 +0300)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Fri, 12 Feb 2010 08:58:39 +0000 (11:58 +0300)
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/CompletionProcessor.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/MethodResolverProcessor.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/PropertyResolverProcessor.java
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/processors/ResolverProcessor.java
plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.java
plugins/groovy/testdata/groovy/completion/IncSmartCompletion.groovy [new file with mode: 0644]

index c590e65b1ea69e808d08b92044bd351f6b949da7..ed726ad7708690b67f3f4c430db0785e81bd0393 100644 (file)
@@ -16,6 +16,7 @@
 package org.jetbrains.plugins.groovy.lang.psi.expectedTypes;
 
 import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.util.PsiTreeUtil;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
@@ -31,6 +32,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatem
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause;
 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.GrUnaryExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
@@ -157,6 +159,24 @@ public class GroovyExpectedTypesProvider {
       myResult = new TypeConstraint[]{SubtypeConstraint.create(trowable)};
     }
 
+    @Override
+    public void visitUnaryExpression(final GrUnaryExpression expression) {
+      TypeConstraint constraint = new TypeConstraint(PsiType.INT) {
+        @Override
+        public boolean satisfied(PsiType type, PsiManager manager, GlobalSearchScope scope) {
+          return TypesUtil
+            .getOverloadedOperatorCandidates(TypesUtil.boxPrimitiveType(type, manager, scope), expression.getOperationTokenType(),
+                                             expression, PsiType.EMPTY_ARRAY).length > 0;
+        }
+
+        @Override
+        public PsiType getDefaultType() {
+          return PsiType.INT;
+        }
+      };
+      myResult = new TypeConstraint[]{constraint};
+    }
+
     public TypeConstraint[] getResult() {
       return myResult;
     }
index 24ab1063a72ce4e0ffd15e3e04ad809b4d2d3ae4..2255134b45ab7675f751025835d309ddb1c60692 100644 (file)
@@ -93,6 +93,28 @@ public class TypesUtil {
 
   @Nullable
   public static PsiType getOverloadedOperatorType(PsiType thisType, String operatorName, GroovyPsiElement place, PsiType[] argumentTypes) {
+    final GroovyResolveResult[] candidates = getOverloadedOperatorCandidates(thisType, operatorName, place, argumentTypes);
+    if (candidates.length == 1) {
+      final PsiElement element = candidates[0].getElement();
+      if (element instanceof PsiMethod) {
+        return candidates[0].getSubstitutor().substitute(((PsiMethod)element).getReturnType());
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  public static GroovyResolveResult[] getOverloadedOperatorCandidates(PsiType thisType,
+                                                                      IElementType tokenType,
+                                                                      GroovyPsiElement place,
+                                                                      PsiType[] argumentTypes) {
+    return getOverloadedOperatorCandidates(thisType, ourOperationsToOperatorNames.get(tokenType), place, argumentTypes);
+  }
+  @NotNull
+  public static GroovyResolveResult[] getOverloadedOperatorCandidates(PsiType thisType,
+                                                                      String operatorName,
+                                                                      GroovyPsiElement place,
+                                                                      PsiType[] argumentTypes) {
     if (operatorName != null) {
       MethodResolverProcessor processor =
         new MethodResolverProcessor(operatorName, place, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY);
@@ -101,21 +123,17 @@ public class TypesUtil {
         final PsiClassType.ClassResolveResult resolveResult = classtype.resolveGenerics();
         final PsiClass lClass = resolveResult.getElement();
         if (lClass != null) {
-          lClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, resolveResult.getSubstitutor()), null, place);
+          lClass
+            .processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, resolveResult.getSubstitutor()), null, place);
         }
       }
 
       ResolveUtil.processNonCodeMethods(thisType, processor, place.getProject(), place, false);
-      final GroovyResolveResult[] candidates = processor.getCandidates();
-      if (candidates.length == 1) {
-        final PsiElement element = candidates[0].getElement();
-        if (element instanceof PsiMethod) {
-          return candidates[0].getSubstitutor().substitute(((PsiMethod)element).getReturnType());
-        }
-      }
+      return processor.getCandidates();
     }
-    return null;
+    return GroovyResolveResult.EMPTY_ARRAY;
   }
+
   private static final Map<IElementType, String> ourPrimitiveTypesToClassNames = new HashMap<IElementType, String>();
   private static final String NULL = "null";
   private static final String JAVA_MATH_BIG_DECIMAL = "java.math.BigDecimal";
index 16209a63aa010eda869331da52efef759b4e32e4..97ad50dbfcbfe4967286faa919ba7761c65c21ac 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiMethod;
 import com.intellij.psi.PsiType;
 import com.intellij.psi.ResolveState;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
 
@@ -53,6 +54,7 @@ public class CompletionProcessor extends ResolverProcessor {
     return new CompletionProcessor(place, EnumSet.of(ResolveKind.PACKAGE, ResolveKind.CLASS), null);
   }
 
+  @NotNull
   public GroovyResolveResult[] getCandidates() {
     if (myCandidates.size() == 0) return GroovyResolveResult.EMPTY_ARRAY;
     return ResolveUtil.filterSameSignatureCandidates(myCandidates);
index d43dcbb5c16f0e31274a27da355b6294bd08b6f7..93ede96f2a4d864882b982faa32a35ff92153232 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.psi.*;
 import com.intellij.psi.scope.JavaScopeProcessorEvent;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
@@ -207,6 +208,7 @@ public class MethodResolverProcessor extends ResolverProcessor {
     return rType;
   }
 
+  @NotNull
   public GroovyResolveResult[] getCandidates() {
     if (!myCandidates.isEmpty()) {
       return filterCandidates();
index 8b23c8277b72610ae208b102b866ce75a923755b..2974c45b0f728ab27a2455c69ddb7abf2dca2c0a 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.Key;
 import com.intellij.psi.*;
 import com.intellij.psi.scope.NameHint;
 import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
@@ -97,6 +98,7 @@ public class PropertyResolverProcessor extends ResolverProcessor {
     return containingClass.findFieldByName(fieldName, false) != null;
   }
 
+  @NotNull
   public GroovyResolveResult[] getCandidates() {
     if (myProperty != null) {
       if (myCandidates.isEmpty()) {
index 21c614da63a5b8f19b736b7641e9d6d74d7f11e6..ce2ca46b45e549cdef0b15dbc32da625cbf13260 100644 (file)
@@ -23,18 +23,19 @@ import com.intellij.psi.scope.NameHint;
 import com.intellij.psi.scope.PsiScopeProcessor;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
-import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
-import static org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint.ResolveKind.*;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
+import static org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint.ResolveKind.*;
+
 /**
  * @author ven
  */
@@ -110,6 +111,7 @@ public class ResolverProcessor implements PsiScopeProcessor, NameHint, ClassHint
     return true;
   }
 
+  @NotNull
   public GroovyResolveResult[] getCandidates() {
     return myCandidates.toArray(new GroovyResolveResult[myCandidates.size()]);
   }
index 641f162090cac376fe036dfec07abaf7da893df2..419fa4733123b83af8961c134e1b6aed5124e612 100644 (file)
@@ -289,4 +289,8 @@ public class GroovyCompletionTest extends LightCodeInsightFixtureTestCase {
   public void testReturnStatement() throws Exception {
     doSmartCompletion("b", "b1", "b2", "foo");
   }
+
+  public void testIncSmartCompletion() throws Exception {
+    doSmartCompletion("a", "b");
+  }
 }
diff --git a/plugins/groovy/testdata/groovy/completion/IncSmartCompletion.groovy b/plugins/groovy/testdata/groovy/completion/IncSmartCompletion.groovy
new file mode 100644 (file)
index 0000000..ffd081a
--- /dev/null
@@ -0,0 +1,15 @@
+class A{
+  def next() {
+    return this;
+  }
+}
+
+class B{
+  def next() {
+    return this;
+  }
+}
+
+A a=new A()
+B b=new B()
+<caret>++;
\ No newline at end of file