diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToStatic/ConvertToStaticProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToStatic/ConvertToStaticProcessor.java index 9c801b9ef6c4..6cf53299f49e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToStatic/ConvertToStaticProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToStatic/ConvertToStaticProcessor.java @@ -1,6 +1,6 @@ -// Copyright 2000-2017 JetBrains s.r.o. -// Use of this source code is governed by the Apache 2.0 license that can be -// found in the LICENSE file. +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ package org.jetbrains.plugins.groovy.refactoring.convertToStatic; import com.intellij.openapi.diagnostic.Logger; @@ -15,9 +15,11 @@ import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.annotator.VisitorCallback; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; @@ -77,7 +79,6 @@ public class ConvertToStaticProcessor extends BaseRefactoringProcessor { for (GroovyFile file : myFiles) { counter++; commitFile(file); - if (file.isScript()) continue; progressIndicator.setText2(file.getName()); progressIndicator.setFraction(counter / (double)myFiles.length); try { @@ -111,12 +112,6 @@ public class ConvertToStaticProcessor extends BaseRefactoringProcessor { } private void putCompileAnnotations(@NotNull GroovyFile file) { - GrTypeDefinition[] classes = file.getTypeDefinitions(); - - for (GrTypeDefinition typeDef : classes) { - addAnnotation(typeDef, true); - } - Set classesWithUnresolvedRef = new HashSet<>(); Set methodsWithUnresolvedRef = new HashSet<>(); @@ -129,18 +124,37 @@ public class ConvertToStaticProcessor extends BaseRefactoringProcessor { GrTypeDefinition containingClass = PsiTreeUtil.getParentOfType(element, GrTypeDefinition.class); if (containingClass != null) classesWithUnresolvedRef.add(containingClass); }; + file.accept(new DynamicFeaturesVisitor(file, myProject, callback)); - for (GrTypeDefinition typeDef : classes) { - processDefinitions(typeDef, classesWithUnresolvedRef, methodsWithUnresolvedRef, false); + file.accept(new GroovyRecursiveElementVisitor() { + @Override + public void visitTypeDefinition(@NotNull GrTypeDefinition typeDef) { + processDefinitions(typeDef, classesWithUnresolvedRef); + super.visitTypeDefinition(typeDef); + } + + @Override + public void visitMethod(@NotNull GrMethod method) { + processMethods(method, methodsWithUnresolvedRef); + } + }); + } + + private void processMethods(@NotNull GrMethod method, Set dynamicMethods) { + boolean isOuterStatic = PsiUtil.isCompileStatic(method.getContainingClass()); + boolean isStatic = dynamicMethods.stream().noneMatch(method::isEquivalentTo); + + if (isOuterStatic != isStatic) { + addAnnotation(method, isStatic); } } private void processDefinitions(GrTypeDefinition typeDef, - Set dynamicClasses, - Set dynamicMethods, - boolean isOuterStatic) { + Set dynamicClasses) { + boolean isOuterStatic = PsiUtil.isCompileStatic(typeDef.getContainingClass()); + boolean isStatic = !dynamicClasses.contains(typeDef); if (isOuterStatic && !isStatic) { addAnnotation(typeDef, false); @@ -148,19 +162,6 @@ public class ConvertToStaticProcessor extends BaseRefactoringProcessor { if (!isOuterStatic && isStatic) { addAnnotation(typeDef, true); } - if (!isOuterStatic && !isStatic) { - removeAnnotation(typeDef); - } - - for (GrMethod method : typeDef.getCodeMethods()) { - if (dynamicMethods.stream().anyMatch(method::isEquivalentTo) == isStatic) { - addAnnotation(method, !isStatic); - } - } - - for (GrTypeDefinition definition : typeDef.getCodeInnerClasses()) { - processDefinitions(definition, dynamicClasses, dynamicMethods, isStatic); - } } private static void applyErrorFixes(@NotNull GroovyFile file) { @@ -195,11 +196,10 @@ public class ConvertToStaticProcessor extends BaseRefactoringProcessor { } } - void removeAnnotation(@NotNull PsiModifierListOwner owner) { + @Nullable + PsiAnnotation findAnnotation(@Nullable PsiModifierListOwner owner, @NotNull String annotation) { + if (owner == null) return null; PsiModifierList modifierList = owner.getModifierList(); - if (modifierList != null) { - PsiAnnotation psiAnnotation = modifierList.findAnnotation(GROOVY_TRANSFORM_COMPILE_STATIC); - if (psiAnnotation != null) psiAnnotation.delete(); - } + return modifierList != null ? modifierList.findAnnotation(annotation) : null; } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/covertToStatic/ConvertTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/covertToStatic/ConvertTest.groovy index 87fcc3561228..2849cd4bb8bf 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/covertToStatic/ConvertTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/covertToStatic/ConvertTest.groovy @@ -1,6 +1,6 @@ -// Copyright 2000-2017 JetBrains s.r.o. -// Use of this source code is governed by the Apache 2.0 license that can be -// found in the LICENSE file. +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ package org.jetbrains.plugins.groovy.refactoring.covertToStatic import org.jetbrains.plugins.groovy.LightGroovyTestCase @@ -37,4 +37,16 @@ class ConvertTest extends LightGroovyTestCase { void testMultiAssignment() { doTest() } + + void testMarkupPrintScript() { + doTest() + } + + void testCompileDynamicClass() { + doTest() + } + + void testNestedClasses() { + doTest() + } } diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes.groovy index 9f40d5536d2a..c331a9d9cbbc 100644 --- a/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes.groovy +++ b/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes.groovy @@ -1,5 +1,5 @@ class Foo { - private field1 = "" + private field1 = bar1() static def bar1() { "" diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes_after.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes_after.groovy index 2de87115e0a6..06a9b744cc71 100644 --- a/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes_after.groovy +++ b/plugins/groovy/testdata/refactoring/convertToStatic/addSkippedTypes_after.groovy @@ -2,7 +2,7 @@ import groovy.transform.CompileStatic @CompileStatic class Foo { - private String field1 = "" + private String field1 = bar1() static String bar1() { "" diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass.groovy new file mode 100644 index 000000000000..16fe0fc34e2f --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass.groovy @@ -0,0 +1,3 @@ +class Foo { + private field1 = new Object().unresolvedMethod() +} diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass_after.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass_after.groovy new file mode 100644 index 000000000000..16fe0fc34e2f --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/compileDynamicClass_after.groovy @@ -0,0 +1,3 @@ +class Foo { + private field1 = new Object().unresolvedMethod() +} diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript.groovy new file mode 100644 index 000000000000..179b885ee7bd --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript.groovy @@ -0,0 +1,39 @@ +import groovy.xml.MarkupBuilder + +class DynamicFeaturesExample { + final ConfigObject config + + DynamicFeaturesExample(ConfigObject config) { + this.config = config + } + + def printXML() { + new MarkupBuilder().records { + persons.each { p -> + person { + name(p.name) + age(p.age) + } + } + } + } + + List getPersons() { + config.persons + } +} + +static def getConfig() { + def c = new ConfigObject() + c.putAll(persons:[[name:'Alex', age: 30]]) + return c +} + +static def addPerson(ConfigObject o) { + o.persons.add([name:"Margaret", age:0]) +} + +def c = getConfig() +addPerson(c) + +new DynamicFeaturesExample(c).printXML() diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript_after.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript_after.groovy new file mode 100644 index 000000000000..94810cbeca46 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/markupPrintScript_after.groovy @@ -0,0 +1,45 @@ +import groovy.transform.CompileDynamic +import groovy.transform.CompileStatic +import groovy.xml.MarkupBuilder + +@CompileStatic +class DynamicFeaturesExample { + final ConfigObject config + + DynamicFeaturesExample(ConfigObject config) { + this.config = config + } + + @CompileDynamic + String printXML() { + new MarkupBuilder().records { + persons.each { p -> + person { + name(p.name) + age(p.age) + } + } + } + } + + @CompileDynamic + List getPersons() { + config.persons + } +} + +@CompileStatic +static ConfigObject getConfig() { + def c = new ConfigObject() + c.putAll(persons:[[name:'Alex', age: 30]]) + return c +} + +static Object addPerson(ConfigObject o) { + o.persons.add([name:"Margaret", age:0]) +} + +def c = getConfig() +addPerson(c) + +new DynamicFeaturesExample(c).printXML() diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses.groovy new file mode 100644 index 000000000000..1c1b3d431724 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses.groovy @@ -0,0 +1,15 @@ +class UpClass { + class InClass{ + def c = UpClass.unresolved + + def method() { + new I() { + def m() { + InClass.unresolved + } + } + } + } +} + +interface I{} diff --git a/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses_after.groovy b/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses_after.groovy new file mode 100644 index 000000000000..854a1f1b1447 --- /dev/null +++ b/plugins/groovy/testdata/refactoring/convertToStatic/nestedClasses_after.groovy @@ -0,0 +1,22 @@ +import groovy.transform.CompileDynamic +import groovy.transform.CompileStatic + +@CompileStatic +class UpClass { + @CompileDynamic + class InClass{ + def c = UpClass.unresolved + + @CompileStatic + def method() { + new I() { + def m() { + InClass.unresolved + } + } + } + } +} + +@CompileStatic +interface I{}