Add "Convert to @CompileStatic" to groovy scripts

This commit is contained in:
alexey.afanasiev
2018-01-23 13:28:48 +03:00
parent 3be1f18c51
commit 48376e6210
10 changed files with 177 additions and 38 deletions
@@ -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<GrTypeDefinition> classesWithUnresolvedRef = new HashSet<>();
Set<GrMethod> 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<GrMethod> 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<GrTypeDefinition> dynamicClasses,
Set<GrMethod> dynamicMethods,
boolean isOuterStatic) {
Set<GrTypeDefinition> 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;
}
}
@@ -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()
}
}
@@ -1,5 +1,5 @@
class Foo {
private field1 = ""
private field1 = bar1()
static def bar1() {
""
@@ -2,7 +2,7 @@ import groovy.transform.CompileStatic
@CompileStatic
class Foo {
private String field1 = ""
private String field1 = bar1()
static String bar1() {
""
@@ -0,0 +1,3 @@
class Foo {
private field1 = new Object().unresolvedMethod()
}
@@ -0,0 +1,3 @@
class Foo {
private field1 = new Object().unresolvedMethod()
}
@@ -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()
@@ -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()
@@ -0,0 +1,15 @@
class UpClass {
class InClass{
def c = UpClass.unresolved
def method() {
new I() {
def m() {
InClass.unresolved
}
}
}
}
}
interface I{}
@@ -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{}