diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java index f1d626e3eeea..6697fcbb68d2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java @@ -109,7 +109,7 @@ public class GroovyConstructorUsagesSearcher extends QueryExecutorBase getConstructorCandidates(PsiClassType classType, + @NotNull GroovyPsiElement context, @Nullable PsiType[] argTypes) { + PsiClass psiClass = classType.resolve(); + if (psiClass == null) return Collections.emptyList(); + + List applicable = CollectionFactory.arrayList(); + final List byName = CollectionFactory.arrayList(); + for (PsiMethod constructor : psiClass.getConstructors()) { + final ResolveResult resolveResult = new PsiElementResolveResult(constructor); + byName.add(resolveResult); + if (argTypes != null && isConstructorCall(classType, argTypes, constructor, context)) { + applicable.add(resolveResult); + } + } + if (applicable.isEmpty()) { + applicable.addAll(byName); + } + return applicable; + } + + @Nullable + public static PsiClassType getTargetConversionType(@NotNull GrExpression expression) { + //todo hack + if (expression.getParent() instanceof GrSafeCastExpression) { + final PsiType type = ((GrSafeCastExpression)expression.getParent()).getType(); + if (type instanceof PsiClassType) { + return (PsiClassType)type; + } + } + if (expression.getParent() instanceof GrTypeCastExpression) { + final PsiType type = ((GrTypeCastExpression)expression.getParent()).getType(); + if (type instanceof PsiClassType) { + return (PsiClassType)type; + } + } + + for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) { + if (type instanceof PsiClassType) { + return (PsiClassType)type; + } + } + return null; + } + @Nullable private PsiType[] argTypes() { final GrListOrMap literal = getElement(); @@ -30,7 +95,7 @@ public class LiteralConstructorReference extends PsiReferenceBase.Poly candidates = getConstructorCandidates(myConstructedClass, literal, argTypes()); + return candidates.toArray(new ResolveResult[candidates.size()]); } @NotNull diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorSearcher.java index 5cf0ef6ddf39..8afac2f50637 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorSearcher.java @@ -1,6 +1,5 @@ package org.jetbrains.plugins.groovy.findUsages; -import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.util.ObjectUtils; @@ -23,7 +22,7 @@ public class LiteralConstructorSearcher { myIncludeOverloads = includeOverloads; } - public boolean processLiteral(GrListOrMap literal, PsiClassType expectedType) { + public boolean processLiteral(GrListOrMap literal) { if (literal.isMap()) { final GrNamedArgument argument = literal.findNamedArgument("super"); if (argument != null) { @@ -31,13 +30,13 @@ public class LiteralConstructorSearcher { } } - return processConstructorReference(new LiteralConstructorReference(literal, expectedType)); + return processConstructorReference(literal.getReference()); } private boolean processConstructorReference(@Nullable PsiReference reference) { if (reference != null && (myIncludeOverloads || reference.isReferenceTo(myConstructor))) { - return myConsumer.process(reference); - } + return myConsumer.process(reference); + } return true; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppReferenceContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppReferenceContributor.java index 91e0f21bb3c4..563ca10694c4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppReferenceContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppReferenceContributor.java @@ -9,54 +9,38 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.CollectionFactory; 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.annotations.Nullable; +import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; 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.expressions.GrSafeCastExpression; -import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; -import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; -import java.util.Collections; import java.util.List; -import java.util.Set; /** * @author peter */ public class GppReferenceContributor extends PsiReferenceContributor { - public static boolean mayInvokeConstructor(PsiClassType expectedType, PsiMethod constructor, GrExpression args) { - if (args == null) { - return true; + @Nullable + private static PsiType[] getSuperConstructorArguments(GrExpression superAttributeValue) { + if (superAttributeValue == null) { + return null; } - final PsiType type = args.getType(); + final PsiType type = superAttributeValue.getType(); if (type == null) { - return true; + return null; } if (type instanceof GrTupleType) { - return isConstructorCall(expectedType, ((GrTupleType)type).getComponentTypes(), constructor, args); + return ((GrTupleType)type).getComponentTypes(); } - return isConstructorCall(expectedType, new PsiType[]{type}, constructor, args); - } - - public static boolean isConstructorCall(PsiClassType expectedType, - PsiType[] argTypes, - PsiMethod constructor, - GroovyPsiElement context) { - for (GroovyResolveResult candidate : PsiUtil.getConstructorCandidates(expectedType, argTypes, context)) { - if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) { - return true; - } - } - return false; + return new PsiType[]{type}; } @Override @@ -90,67 +74,38 @@ public class GppReferenceContributor extends PsiReferenceContributor { public ResolveResult[] multiResolve(boolean incompleteCode) { final GrArgumentLabel context = getElement(); final GrNamedArgument namedArgument = (GrNamedArgument) context.getParent(); - for (PsiType type : getTargetConversionTypes((GrExpression)namedArgument.getParent())) { - if (type instanceof PsiClassType) { - final PsiClassType classType = (PsiClassType)type; - final PsiClass psiClass = classType.resolve(); - if (psiClass != null) { - final GrExpression value = namedArgument.getExpression(); + final PsiClassType classType = LiteralConstructorReference.getTargetConversionType((GrExpression)namedArgument.getParent()); + if (classType != null) { + final PsiClass psiClass = classType.resolve(); + if (psiClass != null) { + final GrExpression value = namedArgument.getExpression(); - final List applicable = addMethodCandidates(classType, value); + final List applicable = addMethodCandidates(classType, value); - final String memberName = getValue(); - if ("super".equals(memberName) && GppTypeConverter.hasTypedContext(myElement)) { - applicable.addAll(addConstructorCandidates(classType, psiClass, value)); - } + final String memberName = getValue(); + if ("super".equals(memberName) && GppTypeConverter.hasTypedContext(myElement)) { + applicable.addAll(LiteralConstructorReference.getConstructorCandidates(classType, context, getSuperConstructorArguments(value))); + } - if (value == null || applicable.isEmpty()) { - final PsiMethod setter = PropertyUtil.findPropertySetter(psiClass, memberName, false, true); - if (setter != null) { - applicable.add(new PsiElementResolveResult(setter)); - } else { - final PsiField field = PropertyUtil.findPropertyField(psiClass.getProject(), psiClass, memberName, false); - if (field != null) { - applicable.add(new PsiElementResolveResult(field)); - } + if (value == null || applicable.isEmpty()) { + final PsiMethod setter = PropertyUtil.findPropertySetter(psiClass, memberName, false, true); + if (setter != null) { + applicable.add(new PsiElementResolveResult(setter)); + } else { + final PsiField field = PropertyUtil.findPropertyField(psiClass.getProject(), psiClass, memberName, false); + if (field != null) { + applicable.add(new PsiElementResolveResult(field)); } } - - return applicable.toArray(new ResolveResult[applicable.size()]); } + + return applicable.toArray(new ResolveResult[applicable.size()]); } } + return ResolveResult.EMPTY_ARRAY; } - private static Set getTargetConversionTypes(GrExpression expression) { - //todo hack - if (expression.getParent() instanceof GrSafeCastExpression) { - final PsiType type = ((GrSafeCastExpression)expression.getParent()).getType(); - if (type != null) { - return Collections.singleton(type); - } - } - - return GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression); - } - - - private static List addConstructorCandidates(PsiClassType classType, PsiClass psiClass, GrExpression value) { - List applicable = CollectionFactory.arrayList(); - final List byName = CollectionFactory.arrayList(); - for (PsiMethod constructor : psiClass.getConstructors()) { - final ResolveResult resolveResult = new PsiElementResolveResult(constructor); - byName.add(resolveResult); - if (mayInvokeConstructor(classType, constructor, value)) { - applicable.add(resolveResult); - } - } - if (applicable.isEmpty()) { - applicable.addAll(byName); - } - return applicable; - } private List addMethodCandidates(PsiClassType classType, GrExpression value) { PsiType valueType = value == null ? null : value.getType(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java index 4e88b3b348ec..7f7705c0d2fa 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java @@ -21,6 +21,8 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; @@ -37,6 +39,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryE import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -142,14 +145,9 @@ public class GroovyExpectedTypesProvider { @Override public void visitOpenBlock(GrOpenBlock block) { - if (block.getParent() instanceof PsiMethod) { - final GrStatement[] statements = block.getStatements(); - if (statements.length > 0 && myExpression.equals(statements[statements.length - 1])) { - final PsiType type = ((PsiMethod)block.getParent()).getReturnType(); - if (type != null) { - myResult = new TypeConstraint[]{new SubtypeConstraint(type, type)}; - } - } + final GrStatement[] statements = block.getStatements(); + if (statements.length > 0 && myExpression.equals(statements[statements.length - 1])) { + checkExitPoint(); } } @@ -157,6 +155,27 @@ public class GroovyExpectedTypesProvider { if (myExpression.equals(ifStatement.getCondition())) { myResult = new TypeConstraint[]{new SubtypeConstraint(TypesUtil.getJavaLangObject(ifStatement), PsiType.BOOLEAN)}; } + else if (myExpression.equals(ifStatement.getThenBranch()) || myExpression.equals(ifStatement.getElseBranch())) { + checkExitPoint(); + } + } + + private void checkExitPoint() { + final PsiElement element = PsiTreeUtil.getParentOfType(myExpression, PsiMethod.class, GrClosableBlock.class); + if (element instanceof GrMethod) { + final GrMethod method = (GrMethod)element; + ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() { + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { + if (returnValue == myExpression) { + final PsiType returnType = method.getReturnType(); + myResult = new TypeConstraint[]{new SubtypeConstraint(returnType, returnType)}; + return false; + } + return true; + } + }); + } } public void visitWhileStatement(GrWhileStatement whileStatement) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java index 77c33eb34ab6..53867b119ecf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java @@ -24,6 +24,7 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; @@ -103,6 +104,16 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { return null; } + @Override + public PsiReference getReference() { + final PsiClassType conversionType = LiteralConstructorReference.getTargetConversionType(this); + if (conversionType != null) { + return new LiteralConstructorReference(this, conversionType); + } + + return null; + } + private static class MyTypesCalculator implements Function { @Nullable public PsiType fun(GrListOrMapImpl listOrMap) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy index 9dc6436c8373..8c61002d1d16 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy @@ -407,6 +407,29 @@ class Point { assertEquals 2, multiResolveReference().size() } + public void testGotoSuperConstructorFromLiteralOnsets() throws Exception { + PsiClass point = myFixture.addClass(""" +class Point { + Point() {} + Point(int y) {} +}""") + + configureGppScript "Point p = [super: 2]" + assertEquals point.constructors[1], resolveReference() + + configureGppScript "Point p = [2]" + assertEquals point.constructors[1], resolveReference() + + configureGppScript "Point p = []" + assertEquals point.constructors[0], resolveReference() + + configureGppScript "Point p = [:]" + assertEquals point.constructors[0], resolveReference() + + configureGppScript "Point p = [239, 42]" + assertEquals 2, multiResolveReference().size() + } + public void testResolveTraitMethod() throws Exception { configureScript """ @Trait diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy index 3e97b48a842c..73a53fb5b441 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy @@ -27,8 +27,14 @@ class LiteralConstructorUsagesTest extends LightCodeInsightFixtureTestCase { Foo() {} } """) - myFixture.addFileToProject "a.groovy", "Foo foo() { if (true) [] else return [] }" - assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size()) + myFixture.addFileToProject "a.groovy", """ +Foo foo() { + if (true) [] + else return [] +} +Foo bar() { [] } +""" + assertEquals(3, ReferencesSearch.search(foo.constructors[0]).findAll().size()) } public void testList_Cast() throws Exception { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/findUsages/FindUsagesTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/findUsages/FindUsagesTest.java index 224db22aa5a1..ed398d693eb6 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/findUsages/FindUsagesTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/findUsages/FindUsagesTest.java @@ -71,7 +71,13 @@ public class FindUsagesTest extends LightGroovyTestCase { } public void testConstructorUsageInNewExpression() throws Throwable { - doTestImpl("ConstructorUsageInNewExpression.groovy", 2); + myFixture.configureByFile("ConstructorUsageInNewExpression.groovy"); + final PsiElement resolved = TargetElementUtilBase.findTargetElement(myFixture.getEditor(), + TargetElementUtilBase.getInstance().getReferenceSearchFlags()); + assertNotNull("Could not resolve reference", resolved); + final GlobalSearchScope projectScope = GlobalSearchScope.projectScope(myFixture.getProject()); + assertEquals(2, MethodReferencesSearch.search((PsiMethod)resolved, projectScope, true).findAll().size()); + assertEquals(4, MethodReferencesSearch.search((PsiMethod)resolved, projectScope, false).findAll().size()); } public void testGotoConstructor() throws Throwable {