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.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());
|
||||
|
||||
-1
@@ -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";
|
||||
|
||||
-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
|
||||
}
|
||||
}
|
||||
|
||||
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"/>
|
||||
|
||||
<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.InheritConstructorContributor"/>
|
||||
<astTransformationSupport implementation="org.jetbrains.plugins.groovy.lang.resolve.ast.builder.strategy.DefaultBuilderStrategySupport"/>
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
+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