diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyCoreEnvironment.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyCoreEnvironment.java
index 0492b241473a..3a90f63ba359 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyCoreEnvironment.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/GroovyCoreEnvironment.java
@@ -93,7 +93,10 @@ import org.jetbrains.plugins.groovy.lang.psi.stubs.index.*;
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.*;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
import org.jetbrains.plugins.groovy.lang.resolve.*;
-import org.jetbrains.plugins.groovy.lang.resolve.ast.*;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.AutoExternalizeContributor;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.ConstructorAnnotationsProcessor;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContributor;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.LoggingContributor;
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport;
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.ExternalBuilderStrategySupport;
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.InitializerBuilderStrategySupport;
@@ -108,6 +111,7 @@ import org.jetbrains.plugins.groovy.swingBuilder.SwingBuilderNamedArgumentProvid
import org.jetbrains.plugins.groovy.swingBuilder.SwingBuilderNonCodeMemberContributor;
import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport;
import org.jetbrains.plugins.groovy.transformations.impl.*;
+import org.jetbrains.plugins.groovy.transformations.impl.autoClone.AutoCloneTransformationSupport;
/**
* Upsource
@@ -143,7 +147,7 @@ public class GroovyCoreEnvironment {
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), AstTransformationSupport.EP_NAME, AstTransformationSupport.class);
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new AutoExternalizeContributor());
- appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new AutoCloneContributor());
+ appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new AutoCloneTransformationSupport());
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new ConstructorAnnotationsProcessor());
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new InheritConstructorContributor());
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new DefaultBuilderStrategySupport());
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java
index 55b68723c0c7..1170174f466f 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java
@@ -46,7 +46,6 @@ public final class GroovyCommonClassNames {
public static final String GROOVY_LANG_REFERENCE = "groovy.lang.Reference";
public static final String JAVA_UTIL_LINKED_HASH_MAP = "java.util.LinkedHashMap";
public static final String GROOVY_TRANSFORM_AUTO_EXTERNALIZE = "groovy.transform.AutoExternalize";
- public static final String GROOVY_TRANSFORM_AUTO_CLONE = "groovy.transform.AutoClone";
public static final String GROOVY_LANG_CATEGORY = "groovy.lang.Category";
public static final String GROOVY_LANG_MIXIN = "groovy.lang.Mixin";
public static final String GROOVY_UTIL_TEST_CASE = "groovy.util.GroovyTestCase";
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AutoCloneContributor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AutoCloneContributor.java
deleted file mode 100644
index c8572cfa476f..000000000000
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/ast/AutoCloneContributor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2000-2016 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.ast;
-
-import com.intellij.psi.PsiModifier;
-import com.intellij.psi.impl.light.LightMethodBuilder;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
-import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport;
-import org.jetbrains.plugins.groovy.transformations.TransformationContext;
-
-/**
- * @author Max Medvedev
- */
-public class AutoCloneContributor implements AstTransformationSupport {
-
- @Override
- public void applyTransformation(@NotNull TransformationContext context) {
- if (!context.hasAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_AUTO_CLONE)) return;
-
- final LightMethodBuilder clone = new LightMethodBuilder(context.getManager(), "clone");
- clone.addModifier(PsiModifier.PUBLIC);
- clone.addException(CloneNotSupportedException.class.getName());
- clone.setOriginInfo("created by @AutoClone");
- context.addMethod(clone);
- }
-}
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/TransformationUtil.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/TransformationUtil.kt
index 277dc9056ff7..95e7d91e5bfb 100644
--- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/TransformationUtil.kt
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/TransformationUtil.kt
@@ -59,3 +59,5 @@ fun isUnderTransformation(clazz: PsiClass?): Boolean {
false
}
}
+
+infix operator fun TransformationContext.plusAssign(method: PsiMethod) = addMethod(method)
diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/autoClone/AutoCloneTransformationSupport.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/autoClone/AutoCloneTransformationSupport.kt
new file mode 100644
index 000000000000..ebc1235b6d47
--- /dev/null
+++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/transformations/impl/autoClone/AutoCloneTransformationSupport.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2000-2016 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.transformations.impl.autoClone
+
+import com.intellij.psi.CommonClassNames
+import com.intellij.psi.PsiEnumConstant
+import com.intellij.psi.PsiModifier
+import com.intellij.psi.PsiType
+import com.intellij.psi.impl.light.LightMethodBuilder
+import org.jetbrains.plugins.groovy.GroovyLanguage
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
+import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil
+import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport
+import org.jetbrains.plugins.groovy.transformations.TransformationContext
+import org.jetbrains.plugins.groovy.transformations.plusAssign
+
+class AutoCloneTransformationSupport : AstTransformationSupport {
+
+ private companion object {
+ val AUTO_CLONE_FQN = "groovy.transform.AutoClone"
+ val ORIGIN_INFO = "created by @AutoClone"
+ val CNSE_FQN = "java.lang.CloneNotSupportedException"
+ }
+
+ override fun applyTransformation(context: TransformationContext) {
+ val annotation = context.getAnnotation(AUTO_CLONE_FQN) ?: return
+
+ context.addInterface(CommonClassNames.JAVA_LANG_CLONEABLE)
+
+ // public T clone() throws CloneNotSupportedException
+ context += LightMethodBuilder(context.manager, "clone").apply {
+ addModifier(PsiModifier.PUBLIC)
+ setMethodReturnType(TypesUtil.createType(context.codeClass))
+ addException(CNSE_FQN)
+ navigationElement = annotation
+ originInfo = ORIGIN_INFO
+ }
+
+ val value = annotation.findDeclaredAttributeValue("style") as? GrReferenceExpression ?: return
+ val constant = value.resolve() as? PsiEnumConstant ?: return
+ if (constant.containingClass?.qualifiedName != "groovy.transform.AutoCloneStyle") return
+ when (constant.name) {
+ "COPY_CONSTRUCTOR" -> {
+ if (context.codeClass.codeConstructors.size == 0) {
+ context += LightMethodBuilder(context.codeClass, GroovyLanguage).apply {
+ isConstructor = true
+ addModifier(PsiModifier.PUBLIC)
+ navigationElement = context.codeClass
+ }
+ }
+
+ // protected T(T other)
+ context += LightMethodBuilder(context.codeClass, GroovyLanguage).apply {
+ isConstructor = true
+ addModifier(PsiModifier.PROTECTED)
+ addParameter("other", TypesUtil.createType(context.codeClass))
+ navigationElement = context.codeClass
+ originInfo = ORIGIN_INFO
+ }
+ }
+ "SIMPLE" -> {
+ // protected void cloneOrCopyMembers(T other) throws CloneNotSupportedException
+ context += LightMethodBuilder(context.manager, "cloneOrCopyMembers").apply {
+ addModifier(PsiModifier.PROTECTED)
+ setMethodReturnType(PsiType.VOID)
+ addParameter("other", TypesUtil.createType(context.codeClass))
+ addException(CNSE_FQN)
+ navigationElement = annotation
+ originInfo = ORIGIN_INFO
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml
index 33e18b57861e..5915c5c783c9 100644
--- a/plugins/groovy/src/META-INF/plugin.xml
+++ b/plugins/groovy/src/META-INF/plugin.xml
@@ -118,7 +118,7 @@
-
+
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy
index beb5c64d5629..4245e37df048 100644
--- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy
+++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy
@@ -730,12 +730,6 @@ class Foo {
assertNotNull(resolve('a.groovy'))
}
- void testAutoClone() {
- def element = resolve('a.groovy', PsiMethod)
- assertTrue element.containingClass.name == 'Foo'
- assertSize 1, element.throwsList.referencedTypes
- }
-
void testDontUseQualifierScopeInDGM() {
assertNull resolve('a.groovy')
}
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/GrAutoCloneTransformationSupportTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/GrAutoCloneTransformationSupportTest.groovy
new file mode 100644
index 000000000000..f8b2af19aff8
--- /dev/null
+++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/transformations/GrAutoCloneTransformationSupportTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2000-2016 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.transformations
+
+import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
+import org.jetbrains.plugins.groovy.LightGroovyTestCase
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
+
+class GrAutoCloneTransformationSupportTest extends LightGroovyTestCase {
+
+ GroovyLightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
+
+ void 'test clone() return type'() {
+ doExpressionTypeTest '''\
+@groovy.transform.AutoClone
+class A1 {}
+new A1().clone()
+''', 'A1'
+ }
+
+ void 'test clone() return type overridden'() {
+ doExpressionTypeTest '''\
+@groovy.transform.AutoClone
+class A1 {}
+@groovy.transform.AutoClone
+class A2 extends A1 {}
+
+new A2().clone()
+''', 'A2'
+ }
+
+ void 'test clone() usage from java'() {
+ myFixture.with {
+ addFileToProject 'Pogo.groovy', '''\
+@groovy.transform.AutoClone
+class Pogo {}
+'''
+ configureByText 'Main.java', '''\
+class Main {
+ void foo() {
+ new Pogo().clone();;
+ try {
+ Pogo pogo = new Pogo().clone();
+ } catch (java.lang.CloneNotSupportedException e) {}
+ }
+}
+'''
+ checkHighlighting()
+ }
+ }
+
+ void 'test copy constructor usage from java'() {
+ myFixture.with {
+ addFileToProject 'Pogo.groovy', '''\
+@groovy.transform.AutoClone(style=groovy.transform.AutoCloneStyle.COPY_CONSTRUCTOR)
+class Pogo {}
+'''
+ configureByText 'Main.java', '''\
+class Main {
+ void foo() {
+ Pogo a = new Pogo();
+ Pogo b = new Pogo(a);
+ }
+}
+'''
+ checkHighlighting()
+ }
+ }
+
+ void 'test cloneOrCopyMembers() usage from java'() {
+ myFixture.with {
+ addFileToProject 'Pogo.groovy', '''\
+@groovy.transform.AutoClone(style=groovy.transform.AutoCloneStyle.SIMPLE)
+class Pogo {}
+'''
+ configureByText 'Pojo.java', '''\
+class Pojo extends Pogo {
+ void foo() {
+ Pogo pogo = new Pogo();
+ cloneOrCopyMembers(pogo);
+ cloneOrCopyMembers();
+ }
+}
+'''
+ checkHighlighting()
+ }
+ }
+
+ private void doExpressionTypeTest(String text, String expectedType) {
+ def file = myFixture.configureByText('_.groovy', text)
+ def ref = file.findReferenceAt(myFixture.editor.caretModel.offset) as GrReferenceExpression
+ def actual = ref.type
+ assertType(expectedType, actual)
+ }
+}
diff --git a/plugins/groovy/testdata/resolve/method/autoClone/a.groovy b/plugins/groovy/testdata/resolve/method/autoClone/a.groovy
deleted file mode 100644
index 45da515d48e2..000000000000
--- a/plugins/groovy/testdata/resolve/method/autoClone/a.groovy
+++ /dev/null
@@ -1,8 +0,0 @@
-import groovy.transform.AutoClone
-
-@AutoClone
-class Foo {
- def foo
-}
-
-print new Foo().clo[ne()
]