don't error-highlight groovy builder members as unresolved
authorpeter <peter@jetbrains.com>
Mon, 30 Jan 2012 17:01:36 +0000 (18:01 +0100)
committerpeter <peter@jetbrains.com>
Tue, 31 Jan 2012 17:30:23 +0000 (18:30 +0100)
plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GroovyUnresolvedAccessInspection.java
plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy
plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy [new file with mode: 0644]

index 91260b7fc7eafe117c2cc83cb59b3c1a568f169f..9b661072510bb582a0390af074f36851b97ea21a 100644 (file)
 
 package org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess;
 
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiPackage;
+import com.intellij.psi.*;
+import com.intellij.util.containers.CollectionFactory;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.annotator.GroovyAnnotator;
 import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
 import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
-import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
+import org.jetbrains.plugins.groovy.gpp.GppTypeConverter;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
 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.util.GroovyCommonClassNames;
+import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
 
+import java.util.Iterator;
+import java.util.List;
+
 import static org.jetbrains.plugins.groovy.annotator.GroovyAnnotator.isDeclarationAssignment;
 
 /**
@@ -72,10 +77,70 @@ public class GroovyUnresolvedAccessInspection extends BaseInspection {
       if (!(parent instanceof GrCall) && ResolveUtil.isKeyOfMap(refExpr)) return; // It's a key of map.
 
       if (!GroovyAnnotator.shouldHighlightAsUnresolved(refExpr)) return;
+      
+      if (qualifier != null && isBuilderInvocation(refExpr)) return;
 
       PsiElement refNameElement = refExpr.getReferenceNameElement();
       registerError(refNameElement == null ? refExpr : refNameElement);
     }
 
   }
+  private static boolean isBuilderInvocation(@NotNull GrReferenceExpression refExpr) {
+    GrExpression qualifier = refExpr.getQualifier();
+    PsiType type = qualifier == null ? null : qualifier.getType();
+    if (type instanceof PsiClassType) {
+      PsiClass target = ((PsiClassType)type).resolve();
+      if (target != null) {
+        for (PsiMethod method : findBuilderMetaMethods(refExpr, target)) {
+          PsiClass containingClass = method.getContainingClass();
+          if (containingClass != null &&
+              method.getParameterList().getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
+            String qname = containingClass.getQualifiedName();
+            if (!GroovyCommonClassNames.GROOVY_OBJECT.equals(qname) && !GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT.equals(qname)) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private static List<PsiMethod> findBuilderMetaMethods(GrReferenceExpression refExpr, PsiClass target) {
+    boolean gpp = GppTypeConverter.hasTypedContext(target) && GppTypeConverter.hasTypedContext(refExpr);
+    if (refExpr.getParent() instanceof GrCall) {
+      List<PsiMethod> toSearch =
+        CollectionFactory.arrayList(target.findMethodsByName(gpp ? "invokeUnresolvedMethod" : "invokeMethod", true));
+      for (Iterator<PsiMethod> iterator = toSearch.iterator(); iterator.hasNext(); ) {
+        PsiMethod method = iterator.next();
+        if (!gpp &&
+            (method.getParameterList().getParametersCount() != 2 || method.getParameterList().getParameters()[1].getType()
+          .equalsToText(CommonClassNames.JAVA_LANG_OBJECT + "[]"))) {
+          iterator.remove();
+        }
+      }
+      return toSearch;
+    }
+
+    if (PsiUtil.isLValue(refExpr)) {
+      List<PsiMethod> toSearch = CollectionFactory.arrayList(target.findMethodsByName(gpp ? "setUnresolvedProperty" : "setProperty", true));
+      for (Iterator<PsiMethod> iterator = toSearch.iterator(); iterator.hasNext(); ) {
+        PsiMethod method = iterator.next();
+        if (method.getParameterList().getParametersCount() != 2 || (!gpp && !method.getParameterList().getParameters()[1].getType()
+          .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) {
+          iterator.remove();
+        }
+      }
+      return toSearch;
+    }
+
+    List<PsiMethod> toSearch = CollectionFactory.arrayList(target.findMethodsByName(gpp ? "getUnresolvedProperty" : "getProperty", true));
+    for (Iterator<PsiMethod> iterator = toSearch.iterator(); iterator.hasNext(); ) {
+      if (iterator.next().getParameterList().getParametersCount() != 1) {
+        iterator.remove();
+      }
+    }
+    return toSearch;
+  }
 }
index 76bf926f58e9e257e8e6c73b7c134308c4b067b4..e72b91eb44d6c82127cb02f96dc322c491174f18 100644 (file)
@@ -452,6 +452,10 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase {
     doTest(new GroovyAssignabilityCheckInspection(), new GroovyUnresolvedAccessInspection());
   }
 
+  public void testBuilderMembersAreNotUnresolved() throws Exception {
+    doTest(new GroovyUnresolvedAccessInspection());
+  }
+
   public void testUnknownVarInArgList() {
     doTest(new GroovyAssignabilityCheckInspection());
   }
diff --git a/plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy b/plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy
new file mode 100644 (file)
index 0000000..300b84e
--- /dev/null
@@ -0,0 +1,22 @@
+class MyBuilder {
+  @Override
+  Object getProperty(String property) {
+    return super.getProperty(property)
+  }
+
+  @Override
+  Object invokeMethod(String name, Object args) {
+    return super.invokeMethod(name, args)
+  }
+
+  @Override
+  void setProperty(String property, Object newValue) {
+    super.setProperty(property, newValue)
+  }
+}
+
+def b = new MyBuilder()
+println b.foo
+println new Object().<warning descr="Can not resolve symbol 'foo'">foo</warning>
+b.foo = 2
+b.bar()
\ No newline at end of file