From 5f49355390c39c5acefc311e4e2b56f579d8e0f1 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 26 Jul 2010 20:31:05 +0100 Subject: [PATCH] find Groovy list2object conversions as constructor usages --- .../GroovyConstructorUsagesSearchHelper.java | 110 +++++++++++++++--- .../lang/LiteralConstructorUsagesTest.groovy | 47 ++++++++ 2 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java index d3ea103a2b8e..9a46dcfc2899 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java @@ -28,18 +28,30 @@ import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; 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.auxiliary.GrListOrMap; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCastExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrTypeCastExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +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.GrTupleType; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; /** * @author Maxim.Medvedev @@ -88,22 +100,7 @@ public class GroovyConstructorUsagesSearchHelper { ReferencesSearch.search(clazz, searchScope, true).forEach(new ReadActionProcessor() { @Override public boolean processInReadAction(PsiReference ref) { - final PsiElement element = ref.getElement(); - if (element instanceof GrCodeReferenceElement) { - GrNewExpression newExpression = null; - if (element.getParent() instanceof GrNewExpression) { - newExpression = (GrNewExpression)element.getParent(); - } - else if (element.getParent() instanceof GrAnonymousClassDefinition) { - newExpression = (GrNewExpression)element.getParent().getParent(); - } - if (newExpression != null) { - final PsiMethod resolvedConstructor = newExpression.resolveConstructor(); - final PsiManager manager = constructor.getManager(); - if (manager.areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) return false; - } - } - return true; + return processClassReference(ref, constructor, consumer); } }); @@ -128,6 +125,85 @@ public class GroovyConstructorUsagesSearchHelper { return true; } + private static boolean processClassReference(PsiReference ref, final PsiMethod constructor, final Processor consumer) { + final PsiElement element = ref.getElement(); + if (!(element instanceof GrCodeReferenceElement)) { + return true; + } + + final PsiElement parent = element.getParent(); + if (parent instanceof GrNewExpression) { + final PsiMethod resolvedConstructor = ((GrNewExpression)parent).resolveConstructor(); + if (constructor.getManager().areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) { + return false; + } + } + else if (parent instanceof GrTypeElement) { + final GrTypeElement typeElement = (GrTypeElement)parent; + + final PsiElement grandpa = typeElement.getParent(); + if (grandpa instanceof GrVariableDeclaration) { + final GrVariable[] vars = ((GrVariableDeclaration)grandpa).getVariables(); + if (vars.length == 1) { + final GrVariable variable = vars[0]; + if (!checkListInstantiation(constructor, consumer, variable.getInitializerGroovy(), typeElement)) { + return false; + } + } + } + else if (grandpa instanceof GrMethod) { + final GrMethod method = (GrMethod)grandpa; + if (typeElement == method.getReturnTypeElementGroovy()) { + ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() { + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { + if (!checkListInstantiation(constructor, consumer, returnValue, typeElement)) { + return false; + } + return true; + } + }); + } + } + else if (grandpa instanceof GrTypeCastExpression) { + final GrTypeCastExpression cast = (GrTypeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + else if (grandpa instanceof GrSafeCastExpression) { + final GrSafeCastExpression cast = (GrSafeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + } + return true; + } + + private static boolean checkListInstantiation(PsiMethod constructor, + Processor consumer, + GrExpression expression, final GrTypeElement typeElement) { + if (expression instanceof GrListOrMap) { + final GrListOrMap list = (GrListOrMap)expression; + if (!list.isMap()) { + final PsiType expectedType = typeElement.getType(); + final PsiType listType = list.getType(); + if (listType instanceof GrTupleType && expectedType instanceof PsiClassType) { + final GroovyResolveResult[] candidates = PsiUtil.getConstructorCandidates((PsiClassType)expectedType, ((GrTupleType)listType).getComponentTypes(), list); + for (GroovyResolveResult candidate : candidates) { + if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) { + if (!consumer.process(PsiReferenceBase.createSelfReference(list, TextRange.from(0, list.getTextLength()), constructor))) { + return false; + } + } + } + } + } + } + return true; + } + private static boolean processConstructors(final PsiMethod constructor, final Processor consumer, final PsiClass clazz, final boolean processThisRefs) { return ApplicationManager.getApplication().runReadAction(new Computable() { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy new file mode 100644 index 000000000000..f6e6df14f708 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy @@ -0,0 +1,47 @@ +package org.jetbrains.plugins.groovy.lang + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.psi.search.searches.ReferencesSearch + +/** + * @author peter + */ +class LiteralConstructorUsagesTest extends LightCodeInsightFixtureTestCase { + + public void testListVariable() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "Foo x = []" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + + public void testListReturnValue() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "Foo foo() { if (true) [] else return [] }" + assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size()) + } + + public void testListCast() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "def x = (Foo) []" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + + public void testListAsCast() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "def x = [] as Foo" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + +}