[Experiment] Automatic rewriting of sends to deprecated methods

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
25 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus
Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Peter Uhnak
Is this based on Mark's Rewrite Tool?

On Sun, Aug 30, 2015 at 10:27 AM, Marcus Denker <[hidden email]> wrote:
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus

Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Mark Rizun

As far as I'm concerned, it's not based on my tool.

30 серп. 2015 3:34 пп "Peter Uhnák" <[hidden email]> пише:
Is this based on Mark's Rewrite Tool?

On Sun, Aug 30, 2015 at 10:27 AM, Marcus Denker <[hidden email]> wrote:
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus

Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Uko2
But it can be integrated with Rewrite Tool ;), it’s not so easy for everyone to write down a rewrite rule from scratch.

Yes, I think that having transformation applied without re-styling will be a huge improvement. Because now there is an “autofix” button, but I don’t use it myself as it restyles all the method.

Uko

On 30 Aug 2015, at 16:02, Mark Rizun <[hidden email]> wrote:

As far as I'm concerned, it's not based on my tool.

30 серп. 2015 3:34 пп "Peter Uhnák" <[hidden email]> пише:
Is this based on Mark's Rewrite Tool?

On Sun, Aug 30, 2015 at 10:27 AM, Marcus Denker <[hidden email]> wrote:
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4
In reply to this post by Peter Uhnak
No, just plain old ParseTreeRewriter.. but integrating Mark’s tool would be very nice as a next step.


On 30 Aug 2015, at 14:34, Peter Uhnák <[hidden email]> wrote:

Is this based on Mark's Rewrite Tool?

On Sun, Aug 30, 2015 at 10:27 AM, Marcus Denker <[hidden email]> wrote:
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

stepharo
But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?

In the future if mark and camille come up with a better rewrite engine then of course we will use it.

Le 30/8/15 18:31, Marcus Denker a écrit :
No, just plain old ParseTreeRewriter.. but integrating Mark’s tool would be very nice as a next step.


On 30 Aug 2015, at 14:34, Peter Uhnák <[hidden email]> wrote:

Is this based on Mark's Rewrite Tool?

On Sun, Aug 30, 2015 at 10:27 AM, Marcus Denker <[hidden email]> wrote:
Hi,

Small experiment:

1) add this method to Object:

deprecated: anExplanationString rule: aRule
        | builder ast rewriteRule method |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        ast := method ast copy.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.

        (rewriteRule executeTree: ast) ifTrue: [
         builder compile: rewriteRule tree formattedCode in: method methodClass classified: method protocol ].
        builder execute.

2) to see it in action, add it to #ifNotNilDo:  in UndefinedObject:

ifNotNilDo: aBlock
        "Please use #ifNotNil: instead"
        self
                deprecated: 'Please use #ifNotNil: instead'
                rule: '`@receiver ifNotNilDo: `@statements'->  '`@receiver ifNotNil: `@statements'.
        ^ self


—> open a browser and see how it fixes code magically.

The only problem is that it does a whole-method refactoring while it should only affect the node
that triggered the deprecation. Else there could be wrong transformations if the same selector
is used but only one of the implementations need to be rewritten, while if we are able to rewrite
the exact sender, we can do it fully automatically.

        Marcus



Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4

> On 31 Aug 2015, at 08:41, stepharo <[hidden email]> wrote:
>
> But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?
>

Yes, it would be nice to have a tool for creating the rules. But then, one can use Mark’s tool as is
for this already now for creating the rewrite rule and then copy-and-paste it.
An integration could make that even easier, but for me this is good enough for now.

> In the future if mark and camille come up with a better rewrite engine then of course we will use it.
>
Yes.

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4
In reply to this post by Marcus Denker-4

>
> The only problem is that it does a whole-method refactoring while it should only affect the node
> that triggered the deprecation. Else there could be wrong transformations if the same selector
> is used but only one of the implementations need to be rewritten, while if we are able to rewrite
> the exact sender, we can do it fully automatically.
>

I think using #sourceNodeExecuted we could do it like this:

deprecated: anExplanationString rule: aRule
        | builder rewriteRule method context node |
        builder := RBCompositeRefactoryChange named: 'deprecation'.
        method := thisContext sender sender method.
        context :=  thisContext sender sender.
        node := context sourceNodeExecuted.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.
        (rewriteRule executeTree: node copy)
                ifTrue: [
                        node replaceWith: rewriteRule tree.
                        builder compile: method ast formattedCode in: method methodClass classified: method protocol ].
        builder execute.


And a question is if we need #formattedCode… all refactoring normally try to update white-space information.
Need to check.

        Marcus
Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

stepharo
In reply to this post by Marcus Denker-4
Yes to create rules. I thought that when the rule was executed and I was
confused.


Le 31/8/15 08:46, Marcus Denker a écrit :

>> On 31 Aug 2015, at 08:41, stepharo <[hidden email]> wrote:
>>
>> But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?
>>
> Yes, it would be nice to have a tool for creating the rules. But then, one can use Mark’s tool as is
> for this already now for creating the rewrite rule and then copy-and-paste it.
> An integration could make that even easier, but for me this is good enough for now.
>
>> In the future if mark and camille come up with a better rewrite engine then of course we will use it.
>>
> Yes.
>
> Marcus
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4
In reply to this post by Marcus Denker-4

> On 31 Aug 2015, at 08:52, Marcus Denker <[hidden email]> wrote:
>
>
>>
>> The only problem is that it does a whole-method refactoring while it should only affect the node
>> that triggered the deprecation. Else there could be wrong transformations if the same selector
>> is used but only one of the implementations need to be rewritten, while if we are able to rewrite
>> the exact sender, we can do it fully automatically.
>>
>
> I think using #sourceNodeExecuted we could do it like this:
>
even simpler:

deprecated: anExplanationString rule: aRule
        |  rewriteRule method context node |
        context :=  thisContext sender sender.
        method := context method.
        node := context sourceNodeExecuted.
        rewriteRule := RBParseTreeRewriter new replace: aRule key with: aRule value.
        (rewriteRule executeTree: node) ifFalse: [ ^self ].
       
        node replaceWith: rewriteRule tree.
        method methodClass compile: method ast formattedCode classified: method protocol.


It seems to work but I have not tested it too much.

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Peter Uhnak
On Mon, Aug 31, 2015 at 8:41 AM, stepharo <[hidden email]> wrote:
But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?

You can use the UI to create the rule, but the application of the rule can be scripted.
 
Yes, it would be nice to have a tool for creating the rules. But then, one can use Mark’s tool as is
for this already now for creating the rewrite rule and then copy-and-paste it.
An integration could make that even easier, but for me this is good enough for now.

RewriteTool creates classes with the rules, so I don't see the need for copy-pasting.
Why not just

self deprecated: anExplanationString rule: aRuleClassGeneratedByRewriteTool

self
Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4

On 31 Aug 2015, at 10:18, Peter Uhnák <[hidden email]> wrote:

On Mon, Aug 31, 2015 at 8:41 AM, stepharo <[hidden email]> wrote:
But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?

You can use the UI to create the rule, but the application of the rule can be scripted.
 
Yes, it would be nice to have a tool for creating the rules. But then, one can use Mark’s tool as is
for this already now for creating the rewrite rule and then copy-and-paste it.
An integration could make that even easier, but for me this is good enough for now.

RewriteTool creates classes with the rules, so I don't see the need for copy-pasting.
Why not just

self deprecated: anExplanationString rule: aRuleClassGeneratedByRewriteTool


good idea, this way the rule is reusable for a code critique rule (for those cases where it
makes sense, e.g. for ifNotNil: yes, as it is likely unique, but for #at: on SmalltalkImage, no ;-)

Marcus

Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

stepharo
In reply to this post by Peter Uhnak
I do not get why you want to link the rewrite engine with deprecation at runtime.
We should not have such dependencies. To me it is insane.

Stef

Le 31/8/15 10:18, Peter Uhnák a écrit :
On Mon, Aug 31, 2015 at 8:41 AM, stepharo <[hidden email]> wrote:
But the tool of mark is a ui tool so why would you want to get a tool to define a rule in your case?

You can use the UI to create the rule, but the application of the rule can be scripted.
 
Yes, it would be nice to have a tool for creating the rules. But then, one can use Mark’s tool as is
for this already now for creating the rewrite rule and then copy-and-paste it.
An integration could make that even easier, but for me this is good enough for now.

RewriteTool creates classes with the rules, so I don't see the need for copy-pasting.
Why not just

self deprecated: anExplanationString rule: aRuleClassGeneratedByRewriteTool

self

Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4

> On 01 Sep 2015, at 08:30, stepharo <[hidden email]> wrote:
>
> I do not get why you want to link the rewrite engine with deprecation at runtime.
> We should not have such dependencies. To me it is insane.
>


It can be made pluggable, of course. I don’t want to have that there in a way that it requires
the refactoring engine to be present all the time.

If it’s there —> transform. If not —> do what it does now.

But just think how much this simplifies the experience of people loading old code into a new
Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
able to evolve the system.

Imagine, this is a functionality (I think) no other system has!

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

demarey

Le 1 sept. 2015 à 08:35, Marcus Denker a écrit :

>
>> On 01 Sep 2015, at 08:30, stepharo <[hidden email]> wrote:
>>
>> I do not get why you want to link the rewrite engine with deprecation at runtime.
>> We should not have such dependencies. To me it is insane.
>>
>
>
> It can be made pluggable, of course. I don’t want to have that there in a way that it requires
> the refactoring engine to be present all the time.
>
> If it’s there —> transform. If not —> do what it does now.
>
> But just think how much this simplifies the experience of people loading old code into a new
> Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
> able to evolve the system.
>
> Imagine, this is a functionality (I think) no other system has!
I will not like a feature where it transforms my code without asking.
I would prefer that a diff browser pops up and asks me if changes are ok. Automatic rewrite of code often forgot to check some specific cases and you end with bugs in your code if there is no review.
The idea is great but the choice of changing the code or not has to be in the user hands.

smime.p7s (5K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4

> On 01 Sep 2015, at 09:30, Christophe Demarey <[hidden email]> wrote:
>
>
> Le 1 sept. 2015 à 08:35, Marcus Denker a écrit :
>
>>
>>> On 01 Sep 2015, at 08:30, stepharo <[hidden email]> wrote:
>>>
>>> I do not get why you want to link the rewrite engine with deprecation at runtime.
>>> We should not have such dependencies. To me it is insane.
>>>
>>
>>
>> It can be made pluggable, of course. I don’t want to have that there in a way that it requires
>> the refactoring engine to be present all the time.
>>
>> If it’s there —> transform. If not —> do what it does now.
>>
>> But just think how much this simplifies the experience of people loading old code into a new
>> Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
>> able to evolve the system.
>>
>> Imagine, this is a functionality (I think) no other system has!
>
> I will not like a feature where it transforms my code without asking.

Yes, I want to have a dialog as a default, too. It should have the possibility to review,
to do your own change *and* to continue and turn off the dialog for future transforms
of this session.

But it is good to know that people don’t think this is that important. The result is that I will
deprecate things with far less guilt using the current scheme ;-)

        Marcus



Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

demarey

Le 1 sept. 2015 à 09:37, Marcus Denker a écrit :

>
>> On 01 Sep 2015, at 09:30, Christophe Demarey <[hidden email]> wrote:
>>
>>
>> Le 1 sept. 2015 à 08:35, Marcus Denker a écrit :
>>
>>>
>>>> On 01 Sep 2015, at 08:30, stepharo <[hidden email]> wrote:
>>>>
>>>> I do not get why you want to link the rewrite engine with deprecation at runtime.
>>>> We should not have such dependencies. To me it is insane.
>>>>
>>>
>>>
>>> It can be made pluggable, of course. I don’t want to have that there in a way that it requires
>>> the refactoring engine to be present all the time.
>>>
>>> If it’s there —> transform. If not —> do what it does now.
>>>
>>> But just think how much this simplifies the experience of people loading old code into a new
>>> Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
>>> able to evolve the system.
>>>
>>> Imagine, this is a functionality (I think) no other system has!
>>
>> I will not like a feature where it transforms my code without asking.
>
> Yes, I want to have a dialog as a default, too. It should have the possibility to review,
> to do your own change *and* to continue and turn off the dialog for future transforms
> of this session.
perfect :)

> But it is good to know that people don’t think this is that important. The result is that I will
> deprecate things with far less guilt using the current scheme ;-)

with the support you will provide for deprecations, for sure, you will be able to deprecate a lot more things. People will care less because there will be support to convert their code.

smime.p7s (5K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Peter Uhnak
In reply to this post by Marcus Denker-4
Yuriy had an idea that QualityAssistant would offer option to rewrite your code, when you are sending a deprecated message.
So you would have 

MyObject>>myMethod
    self deprecated: 'whatevs' rule: RewriteToolRule

and then in your code which sends this method, the QA would inform you of the deprecation and offer you rewrite.
This is safe (it shows Changes dialog first), and practical, because you can publish the rewrite rules as part of your code and users can update easily whenever they are ready.

Peter

On Tue, Sep 1, 2015 at 9:37 AM, Marcus Denker <[hidden email]> wrote:

> On 01 Sep 2015, at 09:30, Christophe Demarey <[hidden email]> wrote:
>
>
> Le 1 sept. 2015 à 08:35, Marcus Denker a écrit :
>
>>
>>> On 01 Sep 2015, at 08:30, stepharo <[hidden email]> wrote:
>>>
>>> I do not get why you want to link the rewrite engine with deprecation at runtime.
>>> We should not have such dependencies. To me it is insane.
>>>
>>
>>
>> It can be made pluggable, of course. I don’t want to have that there in a way that it requires
>> the refactoring engine to be present all the time.
>>
>> If it’s there —> transform. If not —> do what it does now.
>>
>> But just think how much this simplifies the experience of people loading old code into a new
>> Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
>> able to evolve the system.
>>
>> Imagine, this is a functionality (I think) no other system has!
>
> I will not like a feature where it transforms my code without asking.

Yes, I want to have a dialog as a default, too. It should have the possibility to review,
to do your own change *and* to continue and turn off the dialog for future transforms
of this session.

But it is good to know that people don’t think this is that important. The result is that I will
deprecate things with far less guilt using the current scheme ;-)

        Marcus




Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Marcus Denker-4

> On 01 Sep 2015, at 09:51, Peter Uhnák <[hidden email]> wrote:
>
> Yuriy had an idea that QualityAssistant would offer option to rewrite your code, when you are sending a deprecated message.
> So you would have
>
> MyObject>>myMethod
>     self deprecated: 'whatevs' rule: RewriteToolRule
>
> and then in your code which sends this method, the QA would inform you of the deprecation and offer you rewrite.
> This is safe (it shows Changes dialog first), and practical, because you can publish the rewrite rules as part of your code and users can update easily whenever they are ready.
>

Yes!

        Marcus


Reply | Threaded
Open this post in threaded view
|

Re: [Experiment] Automatic rewriting of sends to deprecated methods

Stephan Eggermont-3
In reply to this post by Marcus Denker-4
On 01-09-15 08:35, Marcus Denker wrote:
> But just think how much this simplifies the experience of people loading old code into a new
> Pharo version: it just keeps running and fixes itself! It’s that kind of things we need to be
> able to evolve the system.

This is a crucial step to support continuous improvement indeed.
Just-in-time repeatable refactoring, reducing the cost of change

Stephan


12