mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] support AutoClone copy constructor and simple styles (IDEA-134714)
This commit is contained in:
+6
-2
@@ -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.typeEnhancers.*;
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
|
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
|
||||||
import org.jetbrains.plugins.groovy.lang.resolve.*;
|
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.DefaultBuilderStrategySupport;
|
||||||
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.ExternalBuilderStrategySupport;
|
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.ExternalBuilderStrategySupport;
|
||||||
import org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.InitializerBuilderStrategySupport;
|
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.swingBuilder.SwingBuilderNonCodeMemberContributor;
|
||||||
import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport;
|
import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport;
|
||||||
import org.jetbrains.plugins.groovy.transformations.impl.*;
|
import org.jetbrains.plugins.groovy.transformations.impl.*;
|
||||||
|
import org.jetbrains.plugins.groovy.transformations.impl.autoClone.AutoCloneTransformationSupport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upsource
|
* Upsource
|
||||||
@@ -143,7 +147,7 @@ public class GroovyCoreEnvironment {
|
|||||||
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), AstTransformationSupport.EP_NAME, AstTransformationSupport.class);
|
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), AstTransformationSupport.EP_NAME, AstTransformationSupport.class);
|
||||||
|
|
||||||
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new AutoExternalizeContributor());
|
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 ConstructorAnnotationsProcessor());
|
||||||
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new InheritConstructorContributor());
|
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new InheritConstructorContributor());
|
||||||
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new DefaultBuilderStrategySupport());
|
appEnvironment.addExtension(AstTransformationSupport.EP_NAME, new DefaultBuilderStrategySupport());
|
||||||
|
|||||||
-1
@@ -46,7 +46,6 @@ public final class GroovyCommonClassNames {
|
|||||||
public static final String GROOVY_LANG_REFERENCE = "groovy.lang.Reference";
|
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 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_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_CATEGORY = "groovy.lang.Category";
|
||||||
public static final String GROOVY_LANG_MIXIN = "groovy.lang.Mixin";
|
public static final String GROOVY_LANG_MIXIN = "groovy.lang.Mixin";
|
||||||
public static final String GROOVY_UTIL_TEST_CASE = "groovy.util.GroovyTestCase";
|
public static final String GROOVY_UTIL_TEST_CASE = "groovy.util.GroovyTestCase";
|
||||||
|
|||||||
-40
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
@@ -59,3 +59,5 @@ fun isUnderTransformation(clazz: PsiClass?): Boolean {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix operator fun TransformationContext.plusAssign(method: PsiMethod) = addMethod(method)
|
||||||
|
|||||||
+87
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
<closureMissingMethodContributor implementation="org.jetbrains.plugins.groovy.lang.resolve.PluginXmlClosureMemberContributor"/>
|
<closureMissingMethodContributor implementation="org.jetbrains.plugins.groovy.lang.resolve.PluginXmlClosureMemberContributor"/>
|
||||||
|
|
||||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.AutoExternalizeContributor"/>
|
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.AutoExternalizeContributor"/>
|
||||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.AutoCloneContributor"/>
|
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.transformations.impl.autoClone.AutoCloneTransformationSupport"/>
|
||||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.ConstructorAnnotationsProcessor"/>
|
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.ConstructorAnnotationsProcessor"/>
|
||||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContributor"/>
|
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContributor"/>
|
||||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport"/>
|
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport"/>
|
||||||
|
|||||||
@@ -730,12 +730,6 @@ class Foo {
|
|||||||
assertNotNull(resolve('a.groovy'))
|
assertNotNull(resolve('a.groovy'))
|
||||||
}
|
}
|
||||||
|
|
||||||
void testAutoClone() {
|
|
||||||
def element = resolve('a.groovy', PsiMethod)
|
|
||||||
assertTrue element.containingClass.name == 'Foo'
|
|
||||||
assertSize 1, element.throwsList.referencedTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
void testDontUseQualifierScopeInDGM() {
|
void testDontUseQualifierScopeInDGM() {
|
||||||
assertNull resolve('a.groovy')
|
assertNull resolve('a.groovy')
|
||||||
}
|
}
|
||||||
|
|||||||
+108
@@ -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().clo<caret>ne()
|
||||||
|
''', 'A1'
|
||||||
|
}
|
||||||
|
|
||||||
|
void 'test clone() return type overridden'() {
|
||||||
|
doExpressionTypeTest '''\
|
||||||
|
@groovy.transform.AutoClone
|
||||||
|
class A1 {}
|
||||||
|
@groovy.transform.AutoClone
|
||||||
|
class A2 extends A1 {}
|
||||||
|
|
||||||
|
new A2().clo<caret>ne()
|
||||||
|
''', 'A2'
|
||||||
|
}
|
||||||
|
|
||||||
|
void 'test clone() usage from java'() {
|
||||||
|
myFixture.with {
|
||||||
|
addFileToProject 'Pogo.groovy', '''\
|
||||||
|
@groovy.transform.AutoClone
|
||||||
|
class Pogo {}
|
||||||
|
'''
|
||||||
|
configureByText 'Main.java', '''\
|
||||||
|
class Main {
|
||||||
|
void foo() {
|
||||||
|
<error descr="Unhandled exception: java.lang.CloneNotSupportedException">new Pogo().clone();</error>;
|
||||||
|
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();
|
||||||
|
<error descr="Unhandled exception: java.lang.CloneNotSupportedException">cloneOrCopyMembers(pogo);</error>
|
||||||
|
cloneOrCopyMembers<error descr="'cloneOrCopyMembers(Pogo)' in '' cannot be applied to '()'">()</error>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import groovy.transform.AutoClone
|
|
||||||
|
|
||||||
@AutoClone
|
|
||||||
class Foo {
|
|
||||||
def foo
|
|
||||||
}
|
|
||||||
|
|
||||||
print new Foo().clo<ref>ne()
|
|
||||||
Reference in New Issue
Block a user