-
-
Notifications
You must be signed in to change notification settings - Fork 353
/
RBCondition.class.st
437 lines (384 loc) · 13.6 KB
/
RBCondition.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
"
I am a refactoring condition for doing a precondition check for refactoring operations.
A precondition check returns true or false and is used by refactoring operations to check whether the operation is applicable to the target entity (class or method refactoring).
You can set the block code used for testing the condition (#withBlock:).
And I define some factory methods on my class side for creating instances of me, for some typically usage.
For example:
This creates a condition checking if the class named #Morph implements a selector named #drawOn:
(RBCondition definesSelector:#drawOn: in: (RBClass existingNamed:#Morph)).
Most users of me are refactoring operations and use my methods on the class side for creating instances.
"
Class {
#name : #RBCondition,
#superclass : #RBAbstractCondition,
#instVars : [
'block',
'type',
'errorBlock'
],
#category : #'Refactoring-Core-Conditions'
}
{ #category : #'instance creation' }
RBCondition class >> canUnderstand: aSelector in: aClass [
^self new
type: (Array with: #understandsSelector with: aClass with: aSelector)
block: [aClass canUnderstand: aSelector]
errorString: aClass printString , ' <1?:does not >understand<1?s:> ' , aSelector printString
]
{ #category : #utilities }
RBCondition class >> checkClassVarName: aName in: aClass [
| string |
aName isString ifFalse: [^false].
string := aName asString.
(self reservedNames includes: string) ifTrue: [^false].
string isEmpty ifTrue: [^false].
string first isUppercase ifFalse: [^false].
^RBScanner isVariable: string
]
{ #category : #utilities }
RBCondition class >> checkInstanceVariableName: aName in: aClass [
| string |
aName isString ifFalse: [^false].
string := aName asString.
string isEmpty ifTrue: [^false].
(self reservedNames includes: string) ifTrue: [^false].
string first isUppercase ifTrue: [^false].
^RBScanner isVariable: string
]
{ #category : #utilities }
RBCondition class >> checkMethodName: aString [
"Return whether the argument aName is can represent a selector"
^ aString isString and: [ RBScanner isSelector: aString ]
]
{ #category : #utilities }
RBCondition class >> checkMethodName: aString in: aClass [
"Return whether the argument aName is can represent a selector"
"You probably look for checkMethodName: since the second argument is ignored"
^aString isString and: [ RBScanner isSelector: aString ]
]
{ #category : #'instance creation' }
RBCondition class >> definesClassVariable: aString in: aClass [
^self new
type: (Array
with: #definesClassVar
with: aClass
with: aString)
block: [aClass definesClassVariable: aString]
errorString: aClass printString
, ' <1?:does not >define<1?s:> class variable ' , aString
]
{ #category : #'instance creation' }
RBCondition class >> definesInstanceVariable: aString in: aClass [
^self new
type: (Array
with: #definesInstVar
with: aClass
with: aString)
block: [aClass definesInstanceVariable: aString]
errorString: aClass printString
, ' <1?:does not >define<1?s:> instance variable ' , aString
]
{ #category : #'instance creation' }
RBCondition class >> definesSelector: aSelector in: aClass [
^self new
type: (Array with: #definesSelector with: aClass with: aSelector)
block: [aClass directlyDefinesMethod: aSelector]
errorString: aClass printString , ' <1?:does not >define<1?s:> ' , aSelector printString
]
{ #category : #'instance creation' }
RBCondition class >> definesTempVar: aString in: aClass ignoreClass: subclass [
| condition |
condition := self new.
condition
type: (Array with: #definesTempVarIgnoring with: aClass with: aString with: subclass)
block:
[| method |
method := self
methodDefiningTemporary: aString
in: aClass
ignore: [:class :aSelector | class includesClass: subclass].
method notNil
ifTrue:
[condition errorMacro: method printString , ' defines variable ' , aString].
method notNil]
errorString: aClass printString
, ' <1?:does not >define<1?s:> temporary variable ' , aString.
^condition
]
{ #category : #'instance creation' }
RBCondition class >> definesTemporaryVariable: aString in: aClass [
| condition |
condition := self new.
condition
type: (Array with: #definesTempVar with: aClass with: aString)
block:
[| method |
method := self
methodDefiningTemporary: aString
in: aClass
ignore: [:class :selector | false].
method notNil
ifTrue:
[condition errorMacro: method printString , ' defines variable ' , aString].
method notNil]
errorString: aClass printString
, ' <1?:does not >define<1?s:> temporary variable ' , aString.
^condition
]
{ #category : #'instance creation' }
RBCondition class >> directlyDefinesClassVariable: aString in: aClass [
^self new
type: (Array with: #directlyDefinesClassVar with: aClass with: aString)
block: [aClass directlyDefinesClassVariable: aString]
errorString: aClass printString
, ' <1?:does not >directly define<1?s:> class variable ' , aString
]
{ #category : #'instance creation' }
RBCondition class >> directlyDefinesInstanceVariable: aString in: aClass [
^self new
type: (Array with: #directlyDefinesInstanceVariable with: aClass with: aString)
block: [aClass directlyDefinesInstanceVariable: aString]
errorString: aClass printString
, ' <1?:does not >directly define<1?s:> instance variable ' , aString
]
{ #category : #'instance creation' }
RBCondition class >> empty [
"Returns an empty condition"
^self new
type: (Array with: #empty)
block: [true]
errorString: 'Empty'
]
{ #category : #'instance creation' }
RBCondition class >> hasSubclasses: aClass [
^self new
type: (Array with: #hasSubclasses with: aClass)
block: [aClass subclasses isNotEmpty ]
errorString: aClass printString , ' has <1?:no >subclasses'
]
{ #category : #'instance creation' }
RBCondition class >> hasSubclasses: aClass excluding: classList [
^self new
type: (Array with: #hasSubclasses with: aClass)
block: [ ((aClass subclasses collect: #name) copyWithoutAll: classList) isNotEmpty ]
errorString: aClass printString , ' has <1?:no >subclasses'
]
{ #category : #'instance creation' }
RBCondition class >> hasSuperclass: aClass [
^self new
type: (Array with: #hasSuperclass with: aClass)
block: [aClass superclass isNil not]
errorString: aClass printString , ' has <1?a:no> superclass'
]
{ #category : #'instance creation' }
RBCondition class >> hierarchyOf: aClass canUnderstand: aSelector [
^self new
type: (Array with: #hierarchyUnderstandsSelector with: aClass with: aSelector)
block: [aClass hierarchyDefinesMethod: aSelector]
errorString: aClass printString , ' <1? and/or part of it''s Hierarchy already: and/or part of it''s Hierarchy do not> understand<1?s:> ' , aSelector printString
]
{ #category : #'instance creation' }
RBCondition class >> hierarchyOf: aClass definesVariable: aString [
^self new
type: (Array
with: #hierarchyDefinesInstVar
with: aClass
with: aString)
block: [aClass hierarchyDefinesVariable: aString]
errorString: aClass printString
, ' or one of its subclasses <1?:does not >define<1?s:> variable '
, aString
]
{ #category : #'instance creation' }
RBCondition class >> hierarchyOf: aClass referencesInstanceVariable: aString [
^self new
type: (Array
with: #hierarchyReferencesInstVar
with: aClass
with: aString)
block:
[(aClass withAllSubclasses
detect: [:each | (each whichSelectorsReferToInstanceVariable: aString) isEmpty not]
ifNone: [nil]) notNil]
errorString: aClass printString
, ' or subclass <1?:does not >reference<1?s:> instance variable ' , aString
]
{ #category : #'instance creation' }
RBCondition class >> isAbstractClass: aClass [
^self new
type: (Array with: #IsAbstractClass with: aClass)
block: [aClass isAbstract]
errorString: aClass printString , ' is <1?:not >an abstract class'
]
{ #category : #'instance creation' }
RBCondition class >> isClass: anObject [
^self new
type: (Array with: #IsClass with: anObject)
block: [anObject isBehavior]
errorString: anObject printString , ' is <1?:not >a behavior'
]
{ #category : #'instance creation' }
RBCondition class >> isEmptyClass: anObject [
^self new type: (Array with: #IsEmptyClass with: anObject)
block:
[anObject classVariableNames isEmpty
and: [anObject instanceVariableNames isEmpty and: [anObject selectors isEmpty]]]
errorString: anObject printString , ' is <1?:not > empty'
]
{ #category : #'instance creation' }
RBCondition class >> isGlobal: aString in: aRBSmalltalk [
^self new
type: (Array with: #isGlobal with: aString)
block: [aRBSmalltalk includesGlobal: aString asSymbol]
errorString: aString , ' is <1?:not >a class or global variable'
]
{ #category : #'instance creation' }
RBCondition class >> isImmediateSubclass: subclass of: superClass [
^self new
type: (Array with: #immediateSubclass with: superClass with: subclass)
block: [subclass superclass = superClass]
errorString: subclass printString , ' is <1?:not >an immediate subclass of ' , superClass printString
]
{ #category : #'instance creation' }
RBCondition class >> isMetaclass: anObject [
^self new type: (Array with: #IsMetaclass with: anObject)
block: [anObject isMeta]
errorString: anObject printString , ' is <1?:not >a metaclass'
]
{ #category : #'instance creation' }
RBCondition class >> isSubclass: subclass of: superClass [
^self new
type: (Array with: #subclass with: superClass with: subclass)
block: [subclass includesClass: superClass]
errorString: subclass printString , ' is <1?:not >a subclass of ' , superClass printString
]
{ #category : #'instance creation' }
RBCondition class >> isSymbol: aString [
^self new
type: (Array with: #isSymbol with: aString)
block: [aString isSymbol]
errorString: aString , ' is <1?:not >a symbol'
]
{ #category : #'instance creation' }
RBCondition class >> isValidClassName: aString [
^self new
type: (Array with: #validClassName with: aString)
block: [self validClassName: aString]
errorString: aString , ' is <1?:not >a valid class name'
]
{ #category : #'instance creation' }
RBCondition class >> isValidClassVarName: aString for: aClass [
^self new
type: (Array with: #validClassVarName with: aString with: aClass)
block: [self checkClassVarName: aString in: aClass]
errorString: aString , ' is <1?:not >a valid class variable name'
]
{ #category : #'instance creation' }
RBCondition class >> isValidInstanceVariableName: aString for: aClass [
^self new
type: (Array
with: #validInstVarName
with: aString
with: aClass)
block: [self checkInstanceVariableName: aString in: aClass]
errorString: aString , ' is <1?:not >a valid instance variable name'
]
{ #category : #'instance creation' }
RBCondition class >> isValidMethodName: aString for: aClass [
^self new
type: (Array with: #validMethodName with: aString with: aClass)
block: [self checkMethodName: aString in: aClass]
errorString: aString printString , ' is <1?:not >a valid method name'
]
{ #category : #utilities }
RBCondition class >> methodDefiningTemporary: aString in: aClass ignore: aBlock [
| searcher method |
searcher := RBParseTreeSearcher new.
method := nil. "Shut-up the warning"
searcher matches: aString do: [:aNode :answer | ^method].
aClass withAllSubclasses do:
[:class |
class selectors do:
[:each |
(aBlock value: class value: each)
ifFalse:
[| parseTree |
method := class methodFor: each.
parseTree := class parseTreeFor: each.
parseTree notNil ifTrue: [searcher executeTree: parseTree]]]].
^nil
]
{ #category : #'instance creation' }
RBCondition class >> referencesInstanceVariable: aString in: aClass [
^self new
type: (Array with: #referencesInstVar with: aClass with: aString)
block: [(aClass whichSelectorsReferToInstanceVariable: aString) isEmpty not]
errorString: aClass printString
, ' <1?:does not >reference<1?s:> instance variable ' , aString
]
{ #category : #utilities }
RBCondition class >> reservedNames [
^#('self' 'true' 'false' 'nil' 'thisContext' 'super')
]
{ #category : #'instance creation' }
RBCondition class >> subclassesOf: aClass referToSelector: aSelector [
^self new
type: (Array
with: #subclassReferences
with: aClass
with: aSelector)
block:
[(aClass subclasses detect:
[:each |
(each selectors detect:
[:sel |
| tree |
tree := each parseTreeFor: sel.
tree notNil and: [tree superMessages includes: aSelector]]
ifNone: [nil]) notNil]
ifNone: [nil]) notNil]
errorString: '<1?:no:a> subclass of ' , aClass printString , ' refers to '
, aSelector printString
]
{ #category : #utilities }
RBCondition class >> validClassName: aString [
"Class names and class variable names have the same restrictions"
^self checkClassVarName: aString in: self
]
{ #category : #'instance creation' }
RBCondition class >> withBlock: aBlock [
^self new withBlock: aBlock
]
{ #category : #'instance creation' }
RBCondition class >> withBlock: aBlock errorString: aString [
^self new type: #unknown
block: aBlock
errorString: aString
]
{ #category : #checking }
RBCondition >> check [
^block value
]
{ #category : #initialization }
RBCondition >> errorBlock: anObject [
errorBlock := anObject
]
{ #category : #accessing }
RBCondition >> errorBlockFor: aBoolean [
^errorBlock
]
{ #category : #printing }
RBCondition >> printOn: aStream [
aStream nextPutAll: type asString
]
{ #category : #initialization }
RBCondition >> type: aSymbol block: aBlock errorString: aString [
type := aSymbol.
block := aBlock.
self errorMacro: aString
]
{ #category : #initialization }
RBCondition >> withBlock: aBlock [
block := aBlock.
type := #(#generic)
]