From f5e67a3292190a44bb479f05321719a92940d43d Mon Sep 17 00:00:00 2001 From: Pavel Vojtechovsky Date: Mon, 30 Jan 2017 22:48:24 +0100 Subject: [PATCH] feature: ChangeLocalVariableName + tests --- .../refactoring/ChangeLocalVariableName.java | 52 +++--- .../refactoring/ChangeVariableNameTest.java | 152 ++++++++++++++---- .../testclasses/VariableRename.java | 64 +++++++- 3 files changed, 214 insertions(+), 54 deletions(-) diff --git a/src/main/java/spoon/refactoring/ChangeLocalVariableName.java b/src/main/java/spoon/refactoring/ChangeLocalVariableName.java index 438d1b00fdc..44e19b5ceab 100644 --- a/src/main/java/spoon/refactoring/ChangeLocalVariableName.java +++ b/src/main/java/spoon/refactoring/ChangeLocalVariableName.java @@ -19,15 +19,18 @@ import java.util.List; import java.util.regex.Pattern; +import spoon.reflect.code.CtLambda; import spoon.reflect.code.CtLocalVariable; -import spoon.reflect.code.CtStatementList; import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtExecutable; +import spoon.reflect.declaration.CtVariable; import spoon.reflect.reference.CtReference; import spoon.reflect.visitor.Filter; import spoon.reflect.visitor.chain.CtConsumer; import spoon.reflect.visitor.chain.CtQuery; -import spoon.reflect.visitor.query.VariableReferenceQuery; -import spoon.reflect.visitor.query.VariableScopeQuery; +import spoon.reflect.visitor.filter.ParentFunction; +import spoon.reflect.visitor.filter.VariableReferenceFunction; +import spoon.reflect.visitor.filter.VariableScopeFunction; public class ChangeLocalVariableName extends AbstractRenameRefactor> { @@ -39,37 +42,50 @@ public ChangeLocalVariableName() { @Override protected void forEachReference(CtConsumer consumer) { - getTarget().map(new VariableReferenceQuery()).forEach(consumer); + getTarget().map(new VariableReferenceFunction()).forEach(consumer); } @Override protected void detectNameConflicts(List issues) { - //search for conflicts in scope of parent statement list - CtStatementList l_scope = getTarget().getParent(CtStatementList.class); + //search for parent element, which represents scope of variables, which might be in conflict + //it means first Method, Constructor or AnonymousExecutable. The Lambda is not scope for variables. + CtExecutable l_scope = getTarget().map(new ParentFunction()).select(new Filter>() { + @Override + public boolean matches(CtExecutable element) { + if (element instanceof CtLambda) { + return false; + } + return true; + } + }).first(); //the helper query which searches in scope of input variable for declaration of target variable - CtQuery scopeQuery = target.getFactory().createQuery().map(new VariableScopeQuery().setFilter(new Filter() { + final CtQuery scopeQuery = target.getFactory().createQuery().map(new VariableScopeFunction()).select(new Filter() { public boolean matches(CtElement element) { return element == target; }; - })); - //search for all variable declarations with newName - potential conflict - l_scope.filterChildren(new Filter>() { + }); + //search for all variable declarations whose name is equal to newName => Search for potential name conflict + l_scope.filterChildren(new Filter>() { @Override - public boolean matches(CtLocalVariable var) { + public boolean matches(CtVariable var) { if (newName.equals(var.getSimpleName())) { /* - * visit all elements in scope of input variable - * and match them if they are target variable + * We have found a CtVariable whose simple name is equal to newName of target variable. + * Check if these variables are in conflict by + * visiting all elements in scope of input variable + * and match them if they are target variable. + * In other words, search for declaration of target variable in scope of input variable. + * If it is found, then there is conflict */ - return scopeQuery.setInput(var).list().size() > 0; + return scopeQuery.setInput(var).first() != null; } return false; } }) //called for each variable declaration which is in conflict with newName - .forEach(new CtConsumer>() { + .forEach(new CtConsumer>() { @Override - public void accept(CtLocalVariable conflictVar) { + public void accept(CtVariable conflictVar) { Issue issue = createNameConflictIssue(conflictVar); if (issue != null) { issues.add(issue); @@ -78,7 +94,7 @@ public void accept(CtLocalVariable conflictVar) { }); } - protected Issue createNameConflictIssue(CtLocalVariable conflictVar) { - return new IssueImpl("Local variable with name " + conflictVar.getSimpleName() + " already exists."); + protected Issue createNameConflictIssue(CtVariable conflictVar) { + return new IssueImpl(conflictVar.getSimpleName() + " with name " + conflictVar.getSimpleName() + " already exists."); } } diff --git a/src/test/java/spoon/test/refactoring/ChangeVariableNameTest.java b/src/test/java/spoon/test/refactoring/ChangeVariableNameTest.java index 899d3c35cfd..71d76f58e5a 100644 --- a/src/test/java/spoon/test/refactoring/ChangeVariableNameTest.java +++ b/src/test/java/spoon/test/refactoring/ChangeVariableNameTest.java @@ -2,72 +2,160 @@ import static org.junit.Assert.*; -import java.util.List; - +import org.junit.Before; import org.junit.Test; import spoon.Launcher; import spoon.SpoonException; import spoon.refactoring.ChangeLocalVariableName; -import spoon.refactoring.Refactoring; -import spoon.reflect.code.BinaryOperatorKind; -import spoon.reflect.code.CtBinaryOperator; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.declaration.CtClass; import spoon.reflect.factory.Factory; -import spoon.reflect.visitor.filter.TypeFilter; +import spoon.reflect.visitor.filter.NameFilter; import spoon.test.refactoring.testclasses.VariableRename; public class ChangeVariableNameTest { - @Test - public void testRenameLocalVariable() throws Exception { + Factory factory; + CtClass VariableRenameClass; + CtLocalVariable local1Var; + + @Before + public void setup() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { - "-i", "src/test/java/spoon/test/refactoring/testclasses", - "-o", "target/spooned/refactoring" + "-o", "target/spooned/refactoring", + "--level","info" }); - launcher.buildModel(); - Factory factory = launcher.getFactory(); - CtClass VariableRenameClass = factory.Class().get(VariableRename.class); - - List> localVars = VariableRenameClass.getElements(new TypeFilter>(CtLocalVariable.class)); - CtLocalVariable local1Var = localVars.get(0); + launcher.getEnvironment().setCommentEnabled(true); + launcher.addInputResource("./src/test/java/spoon/test/refactoring/testclasses"); + launcher.run(); + factory = launcher.getFactory(); + VariableRenameClass = factory.Class().get(VariableRename.class); + local1Var = VariableRenameClass.filterChildren(new NameFilter<>("local1")).first(); assertEquals("local1", local1Var.getSimpleName()); + } + + @Test + public void testModelConsistency() throws Exception { + new VariableRename(); + } + + /* + * the pair of String and Boolean. + * The String is new local1 variable name + * The Boolean defines whether this rename must fail on name conflict + */ + Object[] renameConflict = new Object[]{ + "local0", false, + "local1", false, + "local2", false, + "local3", false, + "local4", false, + "local5", false, + "local6", false, + "local7", false, + "local8", false, + "fnc", false, + "values", false, + "param1", false, + "method1", false, + "staticMethod1", false, + "method", false, + }; + + @Test + public void testRenameLocalVariableToUsedName() throws Exception { + ChangeLocalVariableName refactor = new ChangeLocalVariableName(); refactor.setTarget(local1Var); - refactor.setNewName("local2"); - try { - refactor.refactor(); - fail(); - } catch(SpoonException e) { + + int num = renameConflict.length/2; + int i=0; + i=2; num=i+1; + for (; i aClassX = (CtClass) launcher.getFactory().Type().get("spoon.test.refactoring.testclasses.AClassX"); - - final CtBinaryOperator instanceofInvocation = aClassX.getElements(new TypeFilter>(CtBinaryOperator.class)).get(0); - assertEquals(BinaryOperatorKind.INSTANCEOF, instanceofInvocation.getKind()); - assertEquals("o", instanceofInvocation.getLeftHandOperand().toString()); - assertEquals("spoon.test.refactoring.testclasses.AClassX", instanceofInvocation.getRightHandOperand().toString()); } - - } diff --git a/src/test/java/spoon/test/refactoring/testclasses/VariableRename.java b/src/test/java/spoon/test/refactoring/testclasses/VariableRename.java index 3f8458c4f13..de1b9db96e0 100644 --- a/src/test/java/spoon/test/refactoring/testclasses/VariableRename.java +++ b/src/test/java/spoon/test/refactoring/testclasses/VariableRename.java @@ -1,14 +1,70 @@ package spoon.test.refactoring.testclasses; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.junit.Assert; + public class VariableRename { String member1="m1"; static String static1="s1"; - public Object method(String param1) { - String local1 = "l1"; - String local2 = "l2"; - return new Object[]{member1, static1, param1, local1, local2, method1(), staticMethod1()}; + public VariableRename() + { + String[] result = method("p1"); + Assert.assertArrayEquals(new String[]{"l10","l9","m1","s1","p1","l0","l1","l2","l3_0","l4","met1","statMet1","ex1","ex2","l7","l8"}, result); + } + + public String[] method(String param1) { + List values = new ArrayList<>(); + String local10 = "l10"; + values.add(local10); + new Runnable() { + @Override + public void run() { + String local9 = "l9"; + values.add(local9); + Function fnc = (String local7)->{ + String local8 = "l8"; + try { + throw new Exception("ex2"); + } catch (Exception local6) { + values.add(member1); + values.add(static1); + values.add(param1); + { + String local0 = "l0"; + values.add(local0); + } + String local1 = "l1"; + values.add(local1); + String local2 = "l2"; + values.add(local2); + for(int local3 = 0; local3<1; local3++) + { + values.add("l3_"+String.valueOf(local3)); + String local4 = "l4"; + values.add(local4); + } + values.add(method1()); + values.add(staticMethod1()); + try { + throw new Exception("ex1"); + } catch (Exception local5) { + values.add(local5.getMessage()); + } + values.add(local6.getMessage()); + } + values.add(local7); + return local8; + }; + values.add(fnc.apply("l7")); + } + }.run(); + return values.toArray(new String[0]); } public String method1() {