diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 8b085b2dd8cf..1931ece2bfdf 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -51,6 +51,7 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 5db12cf6ed1c..8f6005b61cb6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -91,6 +91,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; +import org.jetbrains.plugins.groovy.lang.resolve.noncode.GrInheritConstructorContributor; import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor; import org.jetbrains.plugins.groovy.overrideImplement.GroovyOverrideImplementUtil; import org.jetbrains.plugins.groovy.overrideImplement.quickFix.ImplementMethodsQuickFix; @@ -289,6 +290,8 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { final PsiClass superClass = typeDefinition.getSuperClass(); if (superClass == null) return; + if (GrInheritConstructorContributor.hasInheritConstructorsAnnotation(typeDefinition)) return; + PsiMethod defConstructor = getDefaultConstructor(superClass); boolean hasImplicitDefConstructor = superClass.getConstructors().length ==0; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java index f09e23bc2fd7..1293b1b7f8f8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java @@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +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.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; @@ -48,6 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClassTypeElement; 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.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils.*; @@ -287,7 +289,14 @@ public class GroovyCompletionUtil { } public static boolean hasConstructorParameters(PsiClass clazz) { - final PsiMethod[] constructors = clazz.getConstructors(); + final PsiMethod[] constructors; + if (clazz instanceof GroovyPsiElement) { + constructors = ResolveUtil.getAllClassConstructors(clazz, (GroovyPsiElement)clazz, PsiSubstitutor.EMPTY); + } + else { + constructors = clazz.getConstructors(); + } + for (PsiMethod constructor : constructors) { if (constructor.getParameterList().getParametersCount() > 0) return true; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyInsertHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyInsertHandler.java index ddf5d564bd12..ffe728bd3e7f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyInsertHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyInsertHandler.java @@ -30,6 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; @@ -37,6 +38,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import java.util.Arrays; @@ -114,7 +116,7 @@ public class GroovyInsertHandler implements InsertHandler { parent.getParent() instanceof GrNewExpression && (offset == text.length() || !text.substring(offset).trim().startsWith("("))) { document.insertString(offset, "()"); - final PsiMethod[] methods = clazz.getConstructors(); + final PsiMethod[] methods = ResolveUtil.getAllClassConstructors(clazz, (GroovyPsiElement)parent, PsiSubstitutor.EMPTY); for (PsiMethod method : methods) { if (method.getParameterList().getParameters().length > 0) { caretModel.moveToOffset(offset + 1); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/handlers/AfterNewClassInsertHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/handlers/AfterNewClassInsertHandler.java index b4006fe9c70e..58c5e4c43d49 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/handlers/AfterNewClassInsertHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/handlers/AfterNewClassInsertHandler.java @@ -28,11 +28,12 @@ import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; /** * @author Maxim.Medvedev @@ -47,13 +48,16 @@ public class AfterNewClassInsertHandler implements InsertHandler item) { - final PsiClass psiClass = PsiUtil.resolveClassInType(myClassType); + final PsiClassType.ClassResolveResult resolveResult = myClassType.resolveGenerics(); + final PsiClass psiClass = resolveResult.getElement(); if (psiClass == null || !psiClass.isValid()) { return; } - - - PsiMethod[] constructors = psiClass.getConstructors(); + PsiElement place = myPlace; + if (!(place instanceof GroovyPsiElement)) { + place = myPlace.getContainingFile(); + } + PsiMethod[] constructors = ResolveUtil.getAllClassConstructors(psiClass, (GroovyPsiElement)place, resolveResult.getSubstitutor()); final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper(); boolean hasParams = ContainerUtil.or(constructors, new Condition() { public boolean value(PsiMethod psiMethod) { @@ -151,8 +155,8 @@ public class AfterNewClassInsertHandler implements InsertHandler(args[i]); } for (int i = 0; i < map.length; i++) { @@ -222,7 +222,7 @@ public class GrClosureSignatureUtil { if (curParam == paramLength) break; if (params[curParam].isOptional()) { - if (TypesUtil.isAssignable(params[curParam].getType(), types[curArg], context, false) && + if (TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), types[curArg], context) && isApplicableInternal(curParam + 1, curArg + 1, false, notOptional)) { map[curParam] = new ArgInfo(args[curArg]); return true; @@ -230,7 +230,7 @@ public class GrClosureSignatureUtil { skipOptionals = true; } else { - if (!TypesUtil.isAssignable(params[curParam].getType(), types[curArg], context, false)) { + if (!TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), types[curArg], context)) { for (int i = startParam; i < curParam; i++) map[i] = null; return false; } @@ -243,7 +243,7 @@ public class GrClosureSignatureUtil { List varargs = new ArrayList(); for (; curArg < args.length; curArg++) { - if (!TypesUtil.isAssignable(vararg, types[curArg], context, false)) { + if (!TypesUtil.isAssignableByMethodCallConversion(vararg, types[curArg], context)) { for (int i = startParam; i < curParam; i++) map[i] = null; return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index da740a065bdf..b1308def84aa 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -51,6 +51,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGd import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor; +import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor; import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -503,4 +504,27 @@ public class ResolveUtil { } return variants; } + + public static GroovyResolveResult[] getNonCodeConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) { + final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass); + final MethodResolverProcessor processor = new MethodResolverProcessor(psiClass.getName(), place, true, null, null, PsiType.EMPTY_ARRAY); + NonCodeMembersContributor + .runContributors(qualifierType, processor, place, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor)); + return processor.getCandidates(); + } + + public static PsiMethod[] getAllClassConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) { + final PsiMethod[] realConstructors = psiClass.getConstructors(); + final GroovyResolveResult[] nonCodeConstructors = getNonCodeConstructors(psiClass, place, substitutor); + PsiMethod[] constructors = new PsiMethod[realConstructors.length + nonCodeConstructors.length]; + System.arraycopy(realConstructors, 0, constructors, 0, realConstructors.length); + for (int i = 0; i < nonCodeConstructors.length; i++) { + GroovyResolveResult nonCodeConstructor = nonCodeConstructors[i]; + final PsiElement element = nonCodeConstructor.getElement(); + if (element instanceof PsiMethod) { + constructors[i + realConstructors.length] = (PsiMethod)element; + } + } + return constructors; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrInheritConstructorContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrInheritConstructorContributor.java new file mode 100644 index 000000000000..cf2222a6c1e1 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/GrInheritConstructorContributor.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2010 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.resolve.noncode; + +import com.intellij.psi.*; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; + +/** + * @author Maxim.Medvedev + */ +public class GrInheritConstructorContributor extends NonCodeMembersContributor { + public static final String INHERIT_CONSTRUCTOR_NAME = "groovy.transform.InheritConstructors"; + + @Override + public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, ResolveState state) { + if (!(qualifierType instanceof PsiClassType)) return; + final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)qualifierType).resolveGenerics(); + final PsiClass psiClass = resolveResult.getElement(); + if (!(psiClass instanceof GrTypeDefinition) || + ((GrTypeDefinition)psiClass).isAnonymous() || + psiClass.isInterface() || + psiClass.isEnum()) { + return; + } + + if (!hasInheritConstructorsAnnotation(psiClass)) return; + + final PsiClass superClass = psiClass.getSuperClass(); + if (superClass == null) return; + final PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); + final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, psiClass, substitutor); + final ResolveState currentState = state.put(PsiSubstitutor.KEY, superClassSubstitutor); + + final PsiMethod[] constructors = superClass.getConstructors(); + for (PsiMethod constructor : constructors) { + if (!processor.execute(constructor, currentState)) return; + } + } + + public static boolean hasInheritConstructorsAnnotation(PsiClass psiClass) { + final PsiModifierList modifierList = psiClass.getModifierList(); + if (modifierList == null) return false; + final PsiAnnotation[] annotations = modifierList.getAnnotations(); + boolean hasInheritConstructors = false; + for (PsiAnnotation annotation : annotations) { + if (INHERIT_CONSTRUCTOR_NAME.equals(annotation.getQualifiedName())) { + hasInheritConstructors = true; + break; + } + } + return hasInheritConstructors; + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.java index dda8d5055d00..d04ccbb008d8 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyCompletionTest.java @@ -293,4 +293,17 @@ public class GroovyCompletionTest extends LightCodeInsightFixtureTestCase { public void testIncSmartCompletion() throws Exception { doSmartCompletion("a", "b"); } + + public void testInheritConstructorsAnnotation() throws Throwable { + myFixture.addFileToProject("groovy/transform/InheritConstructors.java", "package groovy.transform;\n" + + "\n" + + "import java.lang.annotation.ElementType;\n" + + "import java.lang.annotation.Retention;\n" + + "import java.lang.annotation.RetentionPolicy;\n" + + "import java.lang.annotation.Target;@Retention(RetentionPolicy.SOURCE)\n" + + "@Target({ElementType.TYPE})\n" + + "public @interface InheritConstructors {\n" + + "}"); + doSmartTest(); + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy index e05bdbc4687c..5c810d2e9378 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2010 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.gant; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index b505e2061e5a..ccafebd38f7c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -270,4 +270,8 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testSignatureIsNotApplicableToList() throws Exception { doTest(); } + + public void testInheritConstructorsAnnotation() throws Exception { + doTest(); + } } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/util/TestUtils.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/util/TestUtils.java index d8ca5fd0c791..acd4c395d50a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/util/TestUtils.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/util/TestUtils.java @@ -63,7 +63,7 @@ public abstract class TestUtils { } public static String getMockGroovy1_7LibraryName() { - return getMockGroovy1_7LibraryHome()+"/groovy-all-1.7.jar"; + return getMockGroovy1_7LibraryHome()+"/groovy-all-1.7.3.jar"; } public static PsiFile createPseudoPhysicalGroovyFile(final Project project, final String text) throws IncorrectOperationException { diff --git a/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation.groovy b/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation.groovy new file mode 100644 index 000000000000..748ce3a69d4a --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation.groovy @@ -0,0 +1,12 @@ +import groovy.transform.InheritConstructors + +class Base { + def Base(Date x) {} +} + +@InheritConstructors +class Inheritor extends Base { + +} + +Inheritor i = new Inheri \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation_after.groovy b/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation_after.groovy new file mode 100644 index 000000000000..a7712aa4ec28 --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InheritConstructorsAnnotation_after.groovy @@ -0,0 +1,12 @@ +import groovy.transform.InheritConstructors + +class Base { + def Base(Date x) {} +} + +@InheritConstructors +class Inheritor extends Base { + +} + +Inheritor i = new Inheritor() \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/InheritConstructorsAnnotation.groovy b/plugins/groovy/testdata/highlighting/InheritConstructorsAnnotation.groovy new file mode 100644 index 000000000000..3683804d6205 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/InheritConstructorsAnnotation.groovy @@ -0,0 +1,13 @@ +import groovy.transform.InheritConstructors + +class Base { + def Base(T x) {} +} + +@InheritConstructors +class Inheritor extends Base { + +} + +Inheritor i = new Inheritor(new Date()) +Inheritor i2 = new Inheritor(2) diff --git a/plugins/groovy/testdata/mockGroovyLib1.7/groovy-all-1.7.3.jar b/plugins/groovy/testdata/mockGroovyLib1.7/groovy-all-1.7.3.jar new file mode 100644 index 000000000000..4a628324cfa4 Binary files /dev/null and b/plugins/groovy/testdata/mockGroovyLib1.7/groovy-all-1.7.3.jar differ