Skip to content

Commit

Permalink
Merge pull request #82 from jecisc/68-Clean-equals-nil-
Browse files Browse the repository at this point in the history
68-Clean-equals-nil-
  • Loading branch information
jecisc committed May 6, 2020
2 parents feb98f3 + 22bfcc1 commit 725f0a0
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
33 changes: 33 additions & 0 deletions resources/doc/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,36 @@ test

*Warnings:*
This cleaning should not have any counter indication.

### Nil equality cleaner


Chanel replace of nil equality by #isNil or #isNotNil. Here is the list of rewrites:

| Original | Transformation | Reason |
| ------------- | ------------- | ------------- |
| `x = nil` | `x isNil` |
| `x == nil` | `x isNil` |
| `x ~= nil` | `x isNotNil` |
| `x ~~ nil` | `x isNotNil` |

*Conditions for the cleanings to by applied:*
- Can be applied on any classes and traits.
- A pattern from the list above match.
- Does not apply if the application of the pattern would cause an infinit loop. For example it will **not** rewrite:

```Smalltalk
isNil
^ self = nil
```

into:

```Smalltalk
isNil
^ self isNil
```

*Warnings:*
The only danger of this cleaning happens for projects working on multiple Smalltalks or is a project implements ont of those methods and do something else than the ones present in Pharo.

36 changes: 36 additions & 0 deletions src/Chanel-Tests/ChanelCleanersOrderTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,42 @@ ChanelCleanersOrderTest >> testAliasBeforeNilConditionalsBeforeCutConditionalBra
^3'})
]

{ #category : #tests }
ChanelCleanersOrderTest >> testNilEqualityBeforeNilConditionals [
"The alias cleaner needs to run before the nil conditionals cleaner."

class
compile:
('{1}
{2}' format: {self selector . '(10 ~= nil) ifTrue: [ 1 ]'}).

Chanel perfume: {package} using: {ChanelNilConditionalSimplifierCleaner . ChanelNilEqualitySimplifierCleaner}.

self
assert: (class >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10 ifNotNil: [ 1 ]'})
]

{ #category : #tests }
ChanelCleanersOrderTest >> testNilEqualityBeforeNilConditionalsBeforeCutConditionalBranches [
"The nil equality cleaner needs to run before the nil conditionals cleaner which need to run before the cut conditional branches."

class
compile:
('{1}
{2}' format: {self selector . '(10 ~= nil) ifFalse: [ nil ]'}).

Chanel perfume: {package} using: {ChanelNilConditionalSimplifierCleaner . ChanelCutConditionalBranchesCleaner . ChanelNilEqualitySimplifierCleaner}.

self
assert: (class >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10'})
]

{ #category : #tests }
ChanelCleanersOrderTest >> testUnreadTemporariesBeforeUselessASTNodes [
"The cleaner removing the useless AST nodes need to run after the cleaner removing unread temporaries because it will remove more nodes that way."
Expand Down
134 changes: 134 additions & 0 deletions src/Chanel-Tests/ChanelNilEqualitySimplifierCleanerTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"
A ChanelNilEqualitySimplifierCleanerTest is a test class for testing the behavior of ChanelNilEqualitySimplifierCleaner
"
Class {
#name : #ChanelNilEqualitySimplifierCleanerTest,
#superclass : #ChanelAbstractCleanerTest,
#category : #'Chanel-Tests'
}

{ #category : #running }
ChanelNilEqualitySimplifierCleanerTest >> setUp [
super setUp.
class := self createDefaultClass
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testDoesNotReplaceIfItIntroduceAnInfinitLoop [
| oldMethod |
class
compile:
'isNil
^self = nil'.

oldMethod := class >> #isNil.

self runCleaner.

self
assert: (class >> #isNil) sourceCode
equals:
'isNil
^self = nil'.

self assert: class >> #isNil identicalTo: oldMethod
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testEqualsNil [
self assert: '10 = nil' isRewrittenAs: '10 isNil'
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testIdenticalToNil [
self assert: '10 == nil' isRewrittenAs: '10 isNil'
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testNotEqualsNil [
self assert: '10 ~= nil' isRewrittenAs: '10 isNotNil'
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testNotIdenticalToNil [
self assert: '10 ~~ nil' isRewrittenAs: '10 isNotNil'
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testReplacementDoesNotRemoveExtensions [
class
compile:
('{1}
{2}' format: {self selector . '10 = nil'})
classified: self extensionProtocol.

self runCleaner.

self
assert: (class >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10 isNil'}).

self assert: (class >> self selector) protocol equals: self extensionProtocol
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testReplacementInTraits [
| trait |
trait := self createDefaultTrait.

class setTraitComposition: trait.

trait
compile:
('{1}
{2}' format: {self selector . '10 = nil'}).

self runCleaner.

self
assert: (trait >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10 isNil'}).

self assert: (trait localSelectors includes: self selector).
self deny: (class localSelectors includes: self selector)
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testReplacementOnClassSide [
class class
compile:
('{1}
{2}' format: {self selector . '10 = nil'}).

self runCleaner.

self
assert: (class class >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10 isNil'})
]

{ #category : #tests }
ChanelNilEqualitySimplifierCleanerTest >> testWithNothingToReplace [
| oldMethod |
class
compile:
('{1}
{2}' format: {self selector . '10 isNil'}).

oldMethod := class >> self selector.
self runCleaner.

self
assert: (class >> self selector) sourceCode
equals:
('{1}
{2}' format: {self selector . '10 isNil'}).

self assert: class >> self selector identicalTo: oldMethod
]
26 changes: 26 additions & 0 deletions src/Chanel/ChanelNilEqualitySimplifierCleaner.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"
Description
--------------------
I am a cleaner replacing equality to nil by calls to #isNil or #isNotNil.
"
Class {
#name : #ChanelNilEqualitySimplifierCleaner,
#superclass : #ChanelMethodRewriterCleaner,
#category : #Chanel
}

{ #category : #accessing }
ChanelNilEqualitySimplifierCleaner class >> priority [
^ 2500
]

{ #category : #cleaning }
ChanelNilEqualitySimplifierCleaner >> rewriter [
^ RBParseTreeRewriter new
replace: '`@object = nil' with: '`@object isNil';
replace: '`@object == nil' with: '`@object isNil';
replace: '`@object ~= nil' with: '`@object isNotNil';
replace: '`@object ~~ nil' with: '`@object isNotNil';
yourself
]

0 comments on commit 725f0a0

Please sign in to comment.