From 6e1d6e44985c0753c04e992fc3247da824313651 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Fri, 23 Nov 2012 16:26:45 +0400 Subject: [PATCH] IDEA-95249 new WebView(contextMenuEnabled: false) in Groovy highlights 'false' as warning --- ...roovyConstructorNamedArgumentProvider.java | 99 ++++++++++++------- .../lang/GroovyNamedArgumentTest.groovy | 98 ++++++++++++++++++ 2 files changed, 164 insertions(+), 33 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyNamedArgumentTest.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java index 4b09f3f77b31..c8314249c683 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/GroovyConstructorNamedArgumentProvider.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.NameHint; @@ -23,10 +24,8 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.InheritanceUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; import org.jetbrains.plugins.groovy.extensions.GroovyNamedArgumentProvider; import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; -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.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; @@ -41,9 +40,10 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; import java.util.EnumSet; +import java.util.HashMap; import java.util.Map; -import static org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor.*; +import static org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor.Priority; import static org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint.ResolveKind.*; /** @@ -94,13 +94,52 @@ public class GroovyConstructorNamedArgumentProvider extends GroovyNamedArgumentP public static void processClass(@NotNull GrCall call, PsiClassType type, @Nullable String argumentName, - Map result) { + final Map result) { if (argumentName == null) { - ResolveUtil.processAllDeclarations(type, new MyPsiScopeProcessor(result, call), ResolveState.initial(), call); + final HashMap> map = new HashMap>(); + + MyPsiScopeProcessor processor = new MyPsiScopeProcessor() { + @Override + protected void addNamedArgument(String propertyName, PsiType type, PsiElement element) { + if (result.containsKey(propertyName)) return; + + Pair pair = map.get(propertyName); + if (pair != null) { + if (!(element instanceof PsiMethod) && !(element instanceof PsiField)) { // methods should override fields otherwise return + return; + } + } + + map.put(propertyName, Pair.create(type, element)); + } + }; + + processor.setResolveTargetKinds(ResolverProcessor.RESOLVE_KINDS_METHOD_PROPERTY); + + ResolveUtil.processAllDeclarations(type, processor, ResolveState.initial(), call); + + for (Map.Entry> entry : map.entrySet()) { + result.put(entry.getKey(), new NamedArgumentDescriptor.TypeCondition(entry.getValue().first, entry.getValue().getSecond()).setPriority(Priority.AS_LOCAL_VARIABLE)); + } } else { - ResolveUtil.processAllDeclarations(type, new MyPsiScopeProcessor(argumentName, true, result, call), ResolveState.initial(), call); - ResolveUtil.processAllDeclarations(type, new MyPsiScopeProcessor(argumentName, false, result, call), ResolveState.initial(), call); + MyPsiScopeProcessor processor = new MyPsiScopeProcessor() { + @Override + protected void addNamedArgument(String propertyName, PsiType type, PsiElement element) { + if (result.containsKey(propertyName)) return; + result.put(propertyName, new NamedArgumentDescriptor.TypeCondition(type, element).setPriority(Priority.AS_LOCAL_VARIABLE)); + } + }; + + processor.setResolveTargetKinds(ResolverProcessor.RESOLVE_KINDS_METHOD); + processor.setNameHint(GroovyPropertyUtils.getSetterName(argumentName)); + + ResolveUtil.processAllDeclarations(type, processor, ResolveState.initial(), call); + + processor.setResolveTargetKinds(ResolverProcessor.RESOLVE_KINDS_PROPERTY); + processor.setNameHint(argumentName); + + ResolveUtil.processAllDeclarations(type, processor, ResolveState.initial(), call); } } @@ -126,29 +165,9 @@ public class GroovyConstructorNamedArgumentProvider extends GroovyNamedArgumentP return false; } - private static class MyPsiScopeProcessor implements PsiScopeProcessor, NameHint, ClassHint, ElementClassHint { - private final String myNameHint; - private final Map myResult; - private final EnumSet myResolveTargetKinds; - - private MyPsiScopeProcessor(Map result, GroovyPsiElement context) { - myResolveTargetKinds = ResolverProcessor.RESOLVE_KINDS_METHOD_PROPERTY; - myNameHint = null; - myResult = result; - } - - private MyPsiScopeProcessor(@NotNull String propertyName, boolean findSetter, Map result, GroovyPsiElement context) { - if (findSetter) { - myResolveTargetKinds = ResolverProcessor.RESOLVE_KINDS_METHOD; - myNameHint = GroovyPropertyUtils.getSetterName(propertyName); - } - else { - myResolveTargetKinds = ResolverProcessor.RESOLVE_KINDS_PROPERTY; - myNameHint = propertyName; - } - - myResult = result; - } + private static abstract class MyPsiScopeProcessor implements PsiScopeProcessor, NameHint, ClassHint, ElementClassHint { + private String myNameHint; + private EnumSet myResolveTargetKinds; @Override public boolean execute(@NotNull PsiElement element, ResolveState state) { @@ -157,6 +176,8 @@ public class GroovyConstructorNamedArgumentProvider extends GroovyNamedArgumentP PsiType type; if (element instanceof PsiMethod) { + if (!myResolveTargetKinds.contains(METHOD)) return true; + PsiMethod method = (PsiMethod)element; if (!GroovyPropertyUtils.isSimplePropertySetter(method)) return true; @@ -166,25 +187,29 @@ public class GroovyConstructorNamedArgumentProvider extends GroovyNamedArgumentP type = method.getParameterList().getParameters()[0].getType(); } else { + if (!myResolveTargetKinds.contains(PROPERTY)) return true; + type = ((PsiField)element).getType(); propertyName = ((PsiField)element).getName(); } - if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) return true; + if (propertyName.equals(METACLASS)) return true; - if (myResult.containsKey(propertyName) || propertyName.equals(METACLASS)) return true; + if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) return true; PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); if (substitutor != null) { type = substitutor.substitute(type); } - myResult.put(propertyName, new NamedArgumentDescriptor.TypeCondition(type, element).setPriority(Priority.AS_LOCAL_VARIABLE)); + addNamedArgument(propertyName, type, element); } return true; } + protected abstract void addNamedArgument(String propertyName, PsiType type, PsiElement element); + @Override public T getHint(@NotNull Key hintKey) { if ((NameHint.KEY == hintKey && myNameHint != null) || ClassHint.KEY == hintKey || ElementClassHint.KEY == hintKey) { @@ -231,5 +256,13 @@ public class GroovyConstructorNamedArgumentProvider extends GroovyNamedArgumentP public String getName(ResolveState state) { return myNameHint; } + + public void setNameHint(String nameHint) { + myNameHint = nameHint; + } + + public void setResolveTargetKinds(EnumSet resolveTargetKinds) { + myResolveTargetKinds = resolveTargetKinds; + } } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyNamedArgumentTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyNamedArgumentTest.groovy new file mode 100644 index 000000000000..6af6ff94d03d --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyNamedArgumentTest.groovy @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2012 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.psi.CommonClassNames +import com.intellij.psi.PsiType +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil + +/** + * @author Sergey Evdokimov + */ +class GroovyNamedArgumentTest extends LightCodeInsightFixtureTestCase { + + public void testNamedArgumentsFromJavaClass() { + myFixture.addClass(""" +class JavaClass { + public int intField; + private String stringField; + + public static String staticField; + + private int boolProperty; + + public void setBoolProperty(boolean b) { + boolProperty = b ? 1 : 0; + } + + public void getBoolProperty() { + return boolProperty == 1; + } +} +""") + + myFixture.configureByText("a.groovy", "new JavaClass()") + + def caretOffset = myFixture.getCaretOffset() + + def lookUps = myFixture.completeBasic() + + assert lookUps != null + + def context = PsiTreeUtil.getParentOfType(myFixture.file.findElementAt(caretOffset), GroovyPsiElement) + + def allLookupStrings = new HashSet() + + for (def e : lookUps) { + if (e.object instanceof NamedArgumentDescriptor) { + NamedArgumentDescriptor na = e.object + + allLookupStrings << e.lookupString + + if (e.lookupString == "intField") { + assert na.checkType(PsiType.INT, context) + assert na.checkType(PsiType.LONG, context) + assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_INTEGER, context), context) + + assert !na.checkType(PsiType.BOOLEAN, (GroovyPsiElement)context) + assert !na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_STRING, context), context) + } + else if (e.lookupString == "boolProperty") { + assert na.checkType(PsiType.BOOLEAN, context) + assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_BOOLEAN, context), context) + + // todo unkoment this + //assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_STRING, context), context) + //assert na.checkType(PsiType.INT, context) + //assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_OBJECT, context), (GroovyPsiElement)context) + } + else if (e.lookupString == "stringField") { + assert na.checkType(PsiType.INT, (GroovyPsiElement)context) + assert na.checkType(PsiType.BOOLEAN, (GroovyPsiElement)context) + assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_STRING, context), (GroovyPsiElement)context) + assert na.checkType(TypesUtil.createType(CommonClassNames.JAVA_LANG_OBJECT, context), (GroovyPsiElement)context) + } + } + } + + assert allLookupStrings == new HashSet(['intField', 'boolProperty', 'stringField']) + } + +}