Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

review 1: Change local variable name refactoring #1005

Merged
merged 6 commits into from
Mar 22, 2017

Conversation

pvojtechovsky
Copy link
Collaborator

This code is not tested and may be even wrong somewhere. I will work on it, because I will need such refactoring in my project.

Just have a short look at class Refactoring, please. Whether you want such methods in Spoon or it is out of it's scope.

What about checking of validity of such refactoring requests? For example if I have

class A and B in package P and I decide to rename class A to B

then the class B will be twice in the model. Do you have some preferences how to report such invalid requests?
A) to throw exception
B) to call some listener and silently skip the operation
C) ?
I am just sure that in all cases the validity should be checked before the model is modified.

}
});
}

public static void changeVariableName(CtVariable<?> variable, String name) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc

@monperrus
Copy link
Collaborator

Just have a short look at class Refactoring, please.

that's a very useful contribution

Do you have some preferences how to report such invalid requests?

an exception.

we also need strong tests.

@pvojtechovsky
Copy link
Collaborator Author

we also need strong tests.

I like the idea of usage of complex thirdparty library (like guava), with good jUnit tests, as a test environment.

The test would look like this:

  1. rename all local variables to v1, v2, ... vN.
  2. rename all method parameters to v1,v2,...vN
  3. rename all fields to to v1,v2,...vN
  4. rename all classes to v1,v2...vN
    The renaming should detect conflict if name vX is already used in current context and throw exception, then test will try name v(X+1) as long as v(X+Y) is usable name.
    Then we print the refactored model and run the refactored tests on it too. If it pass, then it is OK
    ... but may be it is too complex task, so it will never pass :-) ... we will see

I came with this idea, hoping that I can avoid writing small jUnit test for each refactoring feature. But now I see these small tests are useful when detecting where is the problem in the code.

So the best will be combination of small tests at beginning and at the end that thirparty library refactoring test, which founds the most tricky things.

@monperrus
Copy link
Collaborator

monperrus commented Nov 29, 2016 via email

@monperrus monperrus changed the title New refactoring methods - rename of CtVariable based elements WIP New refactoring methods - rename of CtVariable based elements Jan 11, 2017
@monperrus monperrus changed the title WIP New refactoring methods - rename of CtVariable based elements WIP[test missing] New refactoring methods - rename of CtVariable based elements Jan 11, 2017
@pvojtechovsky
Copy link
Collaborator Author

The refactoring request validity checks are missing.

These checks can be quite tricky. For example rename of local variable should check

  • if there is another local variable with same name defined in visibility scope of that local variable
  • if there is method parameter with same name in current method
  • if there is catch parameter with same name in current catch block

Do you think that we should continue with refactoring functions, which are implemented as static methods of one class? I have feeling like it would be better to have one class for each refactoring. The API might be like this:

interface CtRefactoring {
  boolean isValid();
  void refactor() throws SpoonInvalidRequest;
}

And sometime there are probably some refactorings which cannot be done without temporary inconsistency in the model, so then we should be able to switch OFF the validation check.

I have implemented nothing yet, so I welcome your suggestions. It is easy to accept any design if there is no code yet. ;-)

@monperrus
Copy link
Collaborator

monperrus commented Jan 13, 2017 via email

@pvojtechovsky pvojtechovsky changed the title WIP[test missing] New refactoring methods - rename of CtVariable based elements POC Change local variable name refactoring Jan 17, 2017
@pvojtechovsky
Copy link
Collaborator Author

pvojtechovsky commented Jan 17, 2017

It is POC of Refactoring class, which does rename of local variable.
Usage:

CtLocalVariable target = ...
ChangeLocalVariableName rename = new ChangeLocalVariableName();
rename.setTarget(target);
rename.setNewName("someName");
if(rename.getIssues().size()>0) {
 ... handle conflicts, etc...
}
rename.refactor();

I will add fluent API or a constructor of course. But please give me feedback mainly to

  • Refactor interface
  • Issue interface
  • VariableScopeQuery and VariableReferenceQuery - the nearly finished queries, which can be used for many purposes - they will be used by other future refactorings too ...
  • anything else, what seems to be relevant from your point of view

Please ignore other classes, which comes from other PRs or are obsolete ... I will clean it after your feedback about API

@monperrus
Copy link
Collaborator

Hi Pavel,

This is very interesting but this already contains a lot of different things to be discussed, they may be split in several PRs (hence in several separate discussions, one after the other).

According to your long-term plan, what do you prefer to discuss and merge first? maybe VariableScopeQuery?

@pvojtechovsky
Copy link
Collaborator Author

Hi Martin,

I am aware that there is a lot of things, many baby steps, some of them may be in wrong direction... I develop it this way as a prototype to see what code/structures I need to implement refactoring algorithms in short, efficient and maintainable way. And note, I do not need VariableScopeQuery or VariableReferenceQuery. I need refactorings, which are based on these queries. So I started with refactorings and as a side effect there are VariableScopeQuery and VariableReferenceQuery.

first topic for discussion: VariableScopeQuery?

It is good topic for discussion, because it is one of the core components of that refactorings. So yes, let's discuss it first. I am just not sure if it makes sense to make a PR only for that, because VariableScopeQuery is not much usable on it's own. The better is combination with VariableReferenceQuery. These two are tightly coupled. If you agree then we can move discussion to #1114, which contains only these two queries.

@monperrus
Copy link
Collaborator

If you agree then we can move discussion to #1114, which contains only these two queries.

OK

@pvojtechovsky pvojtechovsky changed the title POC Change local variable name refactoring WiP: Change local variable name refactoring Jan 30, 2017
@pvojtechovsky pvojtechovsky force-pushed the variableRenameRefactoring branch 2 times, most recently from 20db5d7 to 9857e30 Compare February 7, 2017 20:55
@pvojtechovsky
Copy link
Collaborator Author

During implementation of local variable rename refactoring, class ChangeLocalVariableName, I have found that I need to extend CtQuery. I need:

  • to know when query enter/exit an element during filterChildren on any query step level
  • to be able to skip processing of children of current element
  • to be able to skip processing of siblings and children of current elements

I will prepare another PR for that soon. Do you have any preference/ideas how to do it? You can influence the solution before it is implemented - so less effort is needed.

If you want to see context where I need it, then have a look at ChangeLocalVariableName#detectNameConflicts of this PR. There are comments which explains the situation.
This PR depends on PR #1154, so look there for classes, which are missing here.

@monperrus
Copy link
Collaborator

monperrus commented Feb 8, 2017 via email

@pvojtechovsky
Copy link
Collaborator Author

I am testing ChangeLocalVariableName refactoring method using ChangeVariableNameTest. OK, there are still some todos, but I stuck on one thing. The testing algorithm is like this:

  1. take one method from VariableRename class and search for TryRename annotation.
  2. For each found annotation try to rename assigned variable to all new names defined in TryRename annotation. The prefix "-" in the annotation, means that rename to this name should be not possible.
  3. If the rename is possible and passes then the test does following extra steps (see ChangeVariableNameTest#printModelAndTestConsistency)
  4. print the refactored model
  5. compile refactored model
  6. create new class loader and load the class from the refactored model
  7. create instance of that refactored class = call constructor, whose implementation will run all the methods of that class and will check that all assertions are still passing.

The problem is that step 7) does not work. I have no idea why class loader loads some other implementation of the class ...
... it must be so, because otherwise the assertions should fail, because the rename of "var1" to "var2" in this method

	void nestedClassMethodWithShadowVar() {
		@TryRename({"var2", "var3"})
		int var1 = 2; //<--------- the "var1" is renamed to "var2"
		new Runnable() {
			@TryRename({"var1", "var3"})
			int var2 = 3;
			@Override
			public void run() {
				@TryRename({"-var1", "var2"})
				int var3 = 1;
				assertTrue(var1 == 2); //<-----------this reference is renamed too
				//this var1 shadows above defined var1. It can be renamed
				@TryRename({"var2", "-var3"})
				int var1 = 4;
				assertTrue(var1 == 4);
				assertTrue(var2 == 3);
				assertTrue(var3 == 1);
			}
		}.run();
		assertTrue(var1 == 2);
	}

by mistake passes (and it should not pass), and pretty printer produces this valid java code:

    void nestedClassMethodWithShadowVar() {
        @spoon.test.refactoring.testclasses.TryRename(value = { "var2" , "var3" })
        int var2 = 2; //<!----------- is renamed to "var2" - OK
        new java.lang.Runnable() {
            @spoon.test.refactoring.testclasses.TryRename(value = { "var1" , "var3" })
            int var2 = 3;

            @java.lang.Override
            public void run() {
                @spoon.test.refactoring.testclasses.TryRename(value = { "-var1" , "var2" })
                int var3 = 1;
                org.junit.Assert.assertTrue((var2 == 2)); //<-------- is renamed to var2, 
//but the value should be 3, because it is no more local variable reference, but it is now the reference to the class field, 
//which shadows origin local variable! So it should fail here!!!
                @spoon.test.refactoring.testclasses.TryRename(value = { "var2" , "-var3" })
                int var1 = 4;
                org.junit.Assert.assertTrue((var1 == 4));
                org.junit.Assert.assertTrue(((var2) == 3));
                org.junit.Assert.assertTrue((var3 == 1));
            }
        }.run();
        org.junit.Assert.assertTrue((var2 == 2));
    }

but execution of that code does not fail on AssertionError, but it should fail :-(

Any idea why my test class loader does not load classes compiled from refactored code?

@tdurieux
Copy link
Collaborator

tdurieux commented Feb 16, 2017

I could be wrong (I did not read all your changes).
It seams that you compile the original files not the model : https://github.com/INRIA/spoon/pull/1005/files#diff-617f2b7a84783b10a9bd2ed77f0fe739R135

Change: SpoonModelBuilder.InputType.FILES to SpoonModelBuilder.InputType.CTTYPES

@pvojtechovsky
Copy link
Collaborator Author

@tdurieux thanks! It really compiled origin not refactored sources. Now the test fails as expected :-)

This PR is still not finished. There are several bugs ... working on it.

@spoon-bot
Copy link
Collaborator

Revapi Analysis results

Old API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-20170315.164827-83

New API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-SNAPSHOT

Detected changes: 8.

Change 1

Name Element
Old class spoon.reflect.visitor.filter.CatchVariableReferenceFunction
New class spoon.reflect.visitor.filter.CatchVariableReferenceFunction
Code java.class.nonFinalClassInheritsFromNewClass
Description Non-final class now inherits from 'spoon.reflect.visitor.filter.LocalVariableReferenceFunction'.
Breaking source: potentially_breaking, binary: potentially_breaking

Change 1

Name Element
Old class spoon.reflect.visitor.filter.CatchVariableReferenceFunction
New class spoon.reflect.visitor.filter.CatchVariableReferenceFunction
Code java.class.noLongerImplementsInterface
Description Class no longer implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.code.CtCatchVariable<?>>'.
Breaking source: breaking, binary: breaking

Change 2

Name Element
Old class spoon.reflect.visitor.filter.FieldReferenceFunction
New class spoon.reflect.visitor.filter.FieldReferenceFunction
Code java.class.noLongerImplementsInterface
Description Class no longer implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtField<?>>'.
Breaking source: breaking, binary: breaking

Change 2

Name Element
Old class spoon.reflect.visitor.filter.FieldReferenceFunction
New class spoon.reflect.visitor.filter.FieldReferenceFunction
Code java.class.nowImplementsInterface
Description Class now implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: non_breaking, binary: non_breaking

Change 2

Name Element
Old class spoon.reflect.visitor.filter.FieldReferenceFunction
New class spoon.reflect.visitor.filter.FieldReferenceFunction
Code java.class.superTypeTypeParametersChanged
Description Super type's type parameters changed from 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtField<?>>' to 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: potentially_breaking, binary: potentially_breaking

Change 3

Name Element
Old class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
New class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
Code java.class.noLongerImplementsInterface
Description Class no longer implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.code.CtLocalVariable<?>>'.
Breaking source: breaking, binary: breaking

Change 3

Name Element
Old class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
New class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
Code java.class.nowImplementsInterface
Description Class now implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: non_breaking, binary: non_breaking

Change 3

Name Element
Old class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
New class spoon.reflect.visitor.filter.LocalVariableReferenceFunction
Code java.class.superTypeTypeParametersChanged
Description Super type's type parameters changed from 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.code.CtLocalVariable<?>>' to 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: potentially_breaking, binary: potentially_breaking

Change 4

Name Element
Old class spoon.reflect.visitor.filter.ParameterReferenceFunction
New class spoon.reflect.visitor.filter.ParameterReferenceFunction
Code java.class.nonFinalClassInheritsFromNewClass
Description Non-final class now inherits from 'spoon.reflect.visitor.filter.LocalVariableReferenceFunction'.
Breaking source: potentially_breaking, binary: potentially_breaking

Change 4

Name Element
Old class spoon.reflect.visitor.filter.ParameterReferenceFunction
New class spoon.reflect.visitor.filter.ParameterReferenceFunction
Code java.class.noLongerImplementsInterface
Description Class no longer implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtParameter<?>>'.
Breaking source: breaking, binary: breaking

Change 5

Name Element
Old class spoon.reflect.visitor.filter.VariableReferenceFunction
New class spoon.reflect.visitor.filter.VariableReferenceFunction
Code java.class.noLongerImplementsInterface
Description Class no longer implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtVariable<?>>'.
Breaking source: breaking, binary: breaking

Change 5

Name Element
Old class spoon.reflect.visitor.filter.VariableReferenceFunction
New class spoon.reflect.visitor.filter.VariableReferenceFunction
Code java.class.nowImplementsInterface
Description Class now implements interface 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: non_breaking, binary: non_breaking

Change 5

Name Element
Old class spoon.reflect.visitor.filter.VariableReferenceFunction
New class spoon.reflect.visitor.filter.VariableReferenceFunction
Code java.class.superTypeTypeParametersChanged
Description Super type's type parameters changed from 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtVariable<?>>' to 'spoon.reflect.visitor.chain.CtConsumableFunction<spoon.reflect.declaration.CtElement>'.
Breaking source: potentially_breaking, binary: potentially_breaking

Change 6

Name Element
Old parameter void spoon.reflect.visitor.filter.FieldReferenceFunction::apply(===spoon.reflect.declaration.CtField<?>===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
New parameter void spoon.reflect.visitor.filter.FieldReferenceFunction::apply(===spoon.reflect.declaration.CtElement===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
Code java.method.parameterTypeChanged
Description The type of the parameter changed from 'spoon.reflect.declaration.CtField<?>' to 'spoon.reflect.declaration.CtElement'.
Breaking source: potentially_breaking, binary: breaking

Change 7

Name Element
Old parameter void spoon.reflect.visitor.filter.LocalVariableReferenceFunction::apply(===spoon.reflect.code.CtLocalVariable<?>===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
New parameter void spoon.reflect.visitor.filter.LocalVariableReferenceFunction::apply(===spoon.reflect.declaration.CtElement===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
Code java.method.parameterTypeChanged
Description The type of the parameter changed from 'spoon.reflect.code.CtLocalVariable<?>' to 'spoon.reflect.declaration.CtElement'.
Breaking source: potentially_breaking, binary: breaking

Change 8

Name Element
Old parameter void spoon.reflect.visitor.filter.VariableReferenceFunction::apply(===spoon.reflect.declaration.CtVariable<?>===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
New parameter void spoon.reflect.visitor.filter.VariableReferenceFunction::apply(===spoon.reflect.declaration.CtElement===, spoon.reflect.visitor.chain.CtConsumer<java.lang.Object>)
Code java.method.parameterTypeChanged
Description The type of the parameter changed from 'spoon.reflect.declaration.CtVariable<?>' to 'spoon.reflect.declaration.CtElement'.
Breaking source: potentially_breaking, binary: breaking

@pvojtechovsky pvojtechovsky changed the title review 2: Change local variable name refactoring review 1: Change local variable name refactoring Mar 17, 2017
@monperrus
Copy link
Collaborator

rebase to have a cleaner diff?

@pvojtechovsky
Copy link
Collaborator Author

Here you are :-)

Copy link
Collaborator

@monperrus monperrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this brand-new feature, which looks promising.

I've tried to understand the design and usage from the tests and the interface documentation.

Here is a first set of comments.


@Test
public void testModelConsistency() throws Throwable {
new VariableRename();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this have side effects?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has no side effects, but it throws an exception if the tested model is inconsistent. It is similar to VariableReferencesModelTest, where I first tested model consistency by call of constructor, which internally called all other methods of model to check if they contain consistent assertions. Then you came with good idea to switch it to jUnit test class too. So VariableRename is very similar to that, but in this case I cannot easily switch it to jUnit test class, because I am testing model consistency before and after each model refactoring test action.


@Test
public void testRefactorWithoutTarget() throws Exception {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contract: a name refactoring where the given variable name does not exist results in a SpoonException

}

@Test
public void testRenameLocalVariableToInvalidName() throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge all exception tests (incl testRefactorWithoutTarget) in one single test


import static org.junit.Assert.*;

public class VariableRename
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better name? such as RenameTestSubject?

public VariableRename() throws Throwable
{
int local1 = 0;
//call all not private methods of this class automatically, to check assertions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. would be event better in a separate clear method.
new RenameTestSubject().callAllMethods()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public void testRenameLocalVariableToSameName() throws Exception {

ChangeLocalVariableName refactor = new ChangeLocalVariableName();
refactor.setTarget(local1Var);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local1var: what is this?

tests that are self-contained and that can be understood by just looking at the method (without scrolling) are easier to understand an maintain in the long term.


@Test
public void testRenameLocalVariableToUsedName() throws Exception {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the contract tested here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is the main test method (...bad named...), which uses spoon model of VariableRename class. It looks for each CtVariable and it's CtAnnotation and tries to rename that variable to the name defined by annotation.
If the annotation name is prefixed with "-", then that refactoring should fail.
If the annotation name is not prefixed, then that refactoring should pass.

*/
package spoon.refactoring;

public interface Issue {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to RefactoringIssue? If it's an issue, what about using a RefactoringException directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to throw exception directly instead of collecting of issues and then throw an exception?
I would like to support use case where list of issues is better then one exception on the first issue.
The idea is like this:

  1. create an refactoring
  2. call Refactoring#getIssues()
  3. if there are some issues, then solve them automatically, by applying other refactorings
  4. call Refactoring#getIssues() again
  5. if there are still some issues then skip this refactoring
  6. if there are no issues, then process refactoring.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is no problem to remove issues for now and add them later with some mature inheritance hierarchy of issues. So KISS wins here.

/**
*
*/
public interface Refactor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interface and methods must be documented. Can you give a usage snippet? I don't really see here what can be done.

target.setSimpleName(newName);
}

protected abstract void forEachReference(CtConsumer<CtReference> consumer);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc? contract for the implementors?

@pvojtechovsky
Copy link
Collaborator Author

Your ides/change suggestions were implemented. Please review

Copy link
Collaborator

@monperrus monperrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a second set of comments.

I'm really impressed by the test :-)

* r.refactor();
* </pre>
*/
public interface Refactor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to Refactoring?

add method setTarget(CtElement) to really show in the interface that one always refactors something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to Refactoring

but there is already class spoon.refactoring.Refactoring, so ?

add method setTarget(CtElement)

Does all possible refactorings accepts exactly one CtElement? Does all possible refactoring have input called target? I am not sure. I vote to let setTarget method defined on level of more specific refactoring class

import spoon.SpoonException;
import spoon.reflect.declaration.CtNamedElement;

public abstract class AbstractRenameRefactor<T extends CtNamedElement> implements Refactor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently we have only one subclass. what other subclasses do you envision?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RenameTypeRefactor, RenameParameterRefactor, RenameFieldRefactor, RenameMethodRefactor, ...

}
}

private void printModelAndTestConsistency(String refactoringDescription) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is an assert-like method, it should be named accordingly assert*, eg assertCorrectModel

* If it behaves different then expected, then this test fails
*/
@Test
public void testRenameAllLocalVariablesOfRenameTestSubject() throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is super powerful, it's beautiful.

However, I would suggest to add before a simpler test, which does one simple refactoring, it would show a small and clear snippet on how to use RenameLocalVariableRefactor, and it would help to understand this one.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by the way, it's in essence a parametrized test, where RenameLocalVariableRefactorTestSubject contains the test parameters. Really clever. It would be even more explicit and maintainable written as @Parametrized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have experience with parametrized test? Me not. I can learn it, but if you would have time to rewrite this test to parametrized, I might focus on resolving of type parameters. ;-)

* }
* </pre>
*/
public class RenameLocalVariableRefactor extends AbstractRenameRefactor<CtLocalVariable<?>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a static method in Refactoring (eg Refactoring#renameLocalVariable(CtNamedElement target, String newName)) to help discoverability and usage of this class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea. I will do so tomorrow evening.

@monperrus
Copy link
Collaborator

monperrus commented Mar 21, 2017 via email

@monperrus
Copy link
Collaborator

monperrus commented Mar 21, 2017 via email

@monperrus
Copy link
Collaborator

monperrus commented Mar 21, 2017 via email

@pvojtechovsky
Copy link
Collaborator Author

CtRefactor#setTarget

I would prefer to keep CtRefactor as it is. I can imagine refactoring methods, which has no target at all. And how would rename refactoring work on array of target elements?

The problem with that is that the interface cannot be understood without knowing concrete
implementations, which is an issue

But CtRefactor is very abstract. It is more like marker interface, which collects all the future refactoring implementations. I guess the only think which can be "understood" here is that "client can run refactoring after s/he configures it by refactoring specific methods"

I understand that Spoon is interface based, so I have added new interface, where setTarget fits better. What about this one?

public interface CtRenameRefactoring<T extends CtNamedElement> extends CtRefactoring {
	CtRenameRefactoring<T> setTarget(T target);
	CtRenameRefactoring<T> setNewName(String newName);
}

add Refactoring#renameLocalVariable ...

I have added

public static void changeLocalVariableName(CtLocalVariable<?> localVariable, String name)

whose name and parameters fits more to the existing method

public static void changeTypeName(final CtType<?> type, String name)

What about rename of RenameLocalVariableRefactor to
A) CtRenameLocalVariableRefactor
B) CtRenameLocalVariableRefactoring' because it is based on CtRefactoring` interface now.

@spoon-bot
Copy link
Collaborator

Revapi Analysis results

Old API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170321.120037-10

New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT

Detected changes: 1.

Change 1

Name Element
Old method java.util.List<java.io.File> spoon.reflect.cu.CompilationUnit::getBinaryFiles()
New none
Code java.method.removed
Description Method was removed.
Breaking source: breaking, binary: breaking

@spoon-bot
Copy link
Collaborator

Revapi Analysis results

Old API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170321.120037-10

New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT

Detected changes: 1.

Change 1

Name Element
Old method java.util.List<java.io.File> spoon.reflect.cu.CompilationUnit::getBinaryFiles()
New none
Code java.method.removed
Description Method was removed.
Breaking source: breaking, binary: breaking

* @throws RefactoringException when rename to newName would cause model inconsistency, like ambiguity, shadowing of other variables, etc.
*/
public static void changeLocalVariableName(CtLocalVariable<?> localVariable, String name) throws RefactoringException {
new RenameLocalVariableRefactor().setTarget(localVariable).setNewName(name).refactor();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beautiful line

@monperrus
Copy link
Collaborator

monperrus commented Mar 21, 2017 via email

@spoon-bot
Copy link
Collaborator

Revapi Analysis results

Old API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-20170321.234528-11

New API: fr.inria.gforge.spoon:spoon-core:jar:5.7.0-SNAPSHOT

Detected changes: 1.

Change 1

Name Element
Old method java.util.List<java.io.File> spoon.reflect.cu.CompilationUnit::getBinaryFiles()
New none
Code java.method.removed
Description Method was removed.
Breaking source: breaking, binary: breaking

@monperrus monperrus merged commit ae72ad6 into INRIA:master Mar 22, 2017
@monperrus
Copy link
Collaborator

thanks!

@pvojtechovsky
Copy link
Collaborator Author

You are welcome ;-)

@pvojtechovsky pvojtechovsky deleted the variableRenameRefactoring branch March 22, 2017 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants