/
RBMoveTemporaryVariableDefinitionTransformation.class.st
200 lines (168 loc) · 6.06 KB
/
RBMoveTemporaryVariableDefinitionTransformation.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"
Moves the definition of a temporary variable to the block/scope where it is used.
For a temporary variable defined in a method but only initialized and used within a block, the transformation moves the definition to the block which uses this variable.
The transformation automatically searches for the block(s) where the definition could be moved to.
Usage:
| transformation |
transformation := (RBMoveTemporaryVariableDefinitionTransformation
variable: #temp
inMethod: #moveDefinition
inClass: #RBDummyRefactoryTestDataApp)
transform.
(ChangesBrowser changes: transformation model changes changes) open
Preconditions:
- there is a block where the variable definition can be moved to.
"
Class {
#name : #RBMoveTemporaryVariableDefinitionTransformation,
#superclass : #RBCompositeMethodTransformation,
#instVars : [
'variableName',
'blockNodes'
],
#category : #'Refactoring2-Transformations-Model'
}
{ #category : #api }
RBMoveTemporaryVariableDefinitionTransformation class >> model: aRBModel variable: aString inMethod: aSelector inClass: aClass [
^ self new
model: aRBModel;
variable: aString
inMethod: aSelector
inClass: aClass;
yourself
]
{ #category : #api }
RBMoveTemporaryVariableDefinitionTransformation class >> variable: aString inMethod: aSelector inClass: aClass [
^ self new
variable: aString
inMethod: aSelector
inClass: aClass;
yourself
]
{ #category : #converting }
RBMoveTemporaryVariableDefinitionTransformation >> asRefactoring [
^ super asRefactoring
delegatesPreconditions: false;
yourself
]
{ #category : #executing }
RBMoveTemporaryVariableDefinitionTransformation >> buildTransformations [
"ifnil... tight blocks should not be returned as errors"
blockNodes ifNil: [ self checkMethodForBlocks ].
^ (OrderedCollection
withAll: ((blockNodes
sorted: [ :a :b | a start > b start ])
collect: [ :blockNode |
RBAddTemporaryVariableTransformation
variable: variableName
inInterval: blockNode body sourceInterval
inMethod: selector
inClass: class ]))
add: (RBRemoveTemporaryVariableTransformation
variable: variableName
inMethod: selector
inClass: class);
yourself
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> checkAllBlocksIn: aParseTree [
| searcher |
searcher := self parseTreeSearcher.
searcher
matches: '[:`@blockTemps | | `@temps | `@.Statements]'
do: [ :aNode :answer |
(aNode references: variableName)
ifTrue: [ answer add: aNode ].
answer ].
^ searcher
executeTree: aParseTree
initialAnswer: OrderedCollection new
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> checkBlocksIn: aParseTree [
| searcher |
searcher := RBParseTreeSearcher new.
searcher
matches: '[:`@args | | `@temps | `@.Statements]'
do: [ :aNode :answer | answer];
matches: variableName do: [:aNode :answer | true].
^ (searcher executeTree: aParseTree initialAnswer: false)
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> checkLocationsIn: candidateBlocks [
(candidateBlocks
detect: [:each | RBReadBeforeWrittenTester
isVariable: variableName readBeforeWrittenIn: each]
ifNone: [nil]) notNil
ifTrue: [^false].
blockNodes ifNil: [ blockNodes := OrderedCollection new ].
candidateBlocks do: [ :each |
(self checkBlocksIn: each body)
ifTrue: [ blockNodes add: each ]
ifFalse: [ (self checkLocationsIn: (self checkAllBlocksIn: each body))
ifFalse: [ blockNodes add: each ]]].
^ true
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> checkMethodForBlocks [
| definingNode |
definingNode := self definingBlock.
self checkBlocksIn: definingNode.
self checkLocationsIn: (self checkAllBlocksIn: definingNode)
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> definingBlock [
| node definingNode |
node := self definingMethod
ifNil: [ self refactoringError: 'Method does not exist' ]
ifNotNil: [ :methodNode | methodNode variableWith: variableName ].
node ifNil: [self refactoringError: 'Unable to locate temporary variable in parse tree'].
definingNode := node whoDefines: variableName.
definingNode
ifNil: [self refactoringError: 'Cannot locate variable definition'].
definingNode isSequence
ifFalse: [self refactoringError: 'Variable is an argument'].
^ definingNode
]
{ #category : #private }
RBMoveTemporaryVariableDefinitionTransformation >> parseTreeSearcher [
^ RBParseTreeSearcher new
]
{ #category : #preconditions }
RBMoveTemporaryVariableDefinitionTransformation >> preconditions [
^ ( RBCondition
withBlock: [ self definingClass isNotNil ]
errorString: 'No such class or trait named ', class asString)
& ( RBCondition definesSelector: selector in: self definingClass)
& ( RBCondition
withBlock: [ self definingMethod allTemporaryVariables includes: variableName ]
errorString: 'Method named ', selector, ' does not define a temporary variable named ', variableName )
& ( RBCondition
withBlock: [ (self definingMethod allArgumentVariables includes: variableName) not ]
errorString: 'Variable named ', variableName, ' cannot be removed because it is an argument in this method' )
& ( RBCondition
withBlock: [ (self checkBlocksIn: self definingBlock) not ]
errorString: 'Variable named ', variableName, ' is already bound tightly as possible.')
& ( RBCondition
withBlock: [ self checkLocationsIn: (self checkAllBlocksIn: self definingBlock) ]
errorString: 'Variable named ', variableName, ' is used in an outside block.')
]
{ #category : #printing }
RBMoveTemporaryVariableDefinitionTransformation >> storeOn: aStream [
aStream nextPut: $(.
self class storeOn: aStream.
aStream
nextPutAll: ' variable: ''';
nextPutAll: variableName;
nextPutAll: ''' inMethod: ''';
nextPutAll: selector;
nextPutAll: ''' inClass: '.
class storeOn: aStream.
aStream nextPut: $)
]
{ #category : #api }
RBMoveTemporaryVariableDefinitionTransformation >> variable: aVariableName inMethod: aSelector inClass: aClass [
class := aClass.
selector := aSelector.
variableName := aVariableName
]