forked from pharo-project/pharo
/
OCUndeclaredVariableWarning.class.st
244 lines (210 loc) · 7.79 KB
/
OCUndeclaredVariableWarning.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
"
I get signalled when a temporary variable is used that is not defined. My default action is to create an Undeclared binding and add it to the Undeclared dictionary.
"
Class {
#name : #OCUndeclaredVariableWarning,
#superclass : #OCSemanticWarning,
#category : #'OpalCompiler-Core-Exception'
}
{ #category : #correcting }
OCUndeclaredVariableWarning >> declareClassVar [
self methodClass instanceSide
addClassVarNamed: node name asSymbol.
^ self lookUpVariable
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> declareGlobal [
Smalltalk at: node name asSymbol put: nil.
^self lookUpVariable
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> declareInstVar: name [
"Declare an instance variable."
self methodClass addInstVarNamed: name.
"We are changing a class after the scope hierarchy was created, so we need to update the
Instance Scope"
self methodNode scope instanceScope slots: self methodClass allSlots.
^ self lookUpVariable
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> declareTempAndPaste: name [
| insertion delta theTextString characterBeforeMark tempsMark newMethodNode |
"Below we are getting the text that is actually seen in the morph. This is rather ugly. Maybe there is a better way to do this."
theTextString := self requestor textMorph editor paragraph text.
"We parse again the method displayed in the morph. The variable methodNode has the first version of the method, without temporary declarations. "
newMethodNode := RBParser parseMethod: theTextString.
"We check if there is a declaration of temporary variables"
tempsMark := newMethodNode body rightBar ifNil: [ self methodNode body start ].
characterBeforeMark := theTextString at: tempsMark-1 ifAbsent: [$ ].
(theTextString at: tempsMark) = $| ifTrue: [
"Paste it before the second vertical bar"
insertion := name, ' '.
characterBeforeMark isSeparator ifFalse: [insertion := ' ', insertion].
delta := 0.
] ifFalse: [
"No bars - insert some with CR, tab"
insertion := '| ' , name , ' |',String cr.
delta := 2. "the bar and CR"
characterBeforeMark = Character tab ifTrue: [
insertion := insertion , String tab.
delta := delta + 1. "the tab" ]
].
tempsMark := tempsMark +
(self substituteWord: insertion
wordInterval: (tempsMark to: tempsMark-1)
offset: 0) - delta.
" we can not guess at this point where the tempvar should be stored,
tempvars vs. tempvector therefore -> reparse"
(ReparseAfterSourceEditing new newSource: self requestor text) signal
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> declareUndefined [
^UndeclaredVariable registeredWithName: node name
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> defaultAction [
| varName className selector var |
className := self methodClass name .
selector := self methodNode selector.
varName := node name.
NewUndeclaredWarning signal: varName in: (self methodNode selector
ifNotNil: [className, '>>', selector]
ifNil: ['<unknown>']).
^super defaultAction ifNil: [ self declareUndefined ]
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> defineClass: className [
"Prompts the user to define a new class."
| classSymbol systemCategory classDefinition |
classSymbol := className asSymbol.
systemCategory := self methodClass category
ifNil: [ 'Unknown' ].
classDefinition := 'Object subclass: #' , classSymbol , '
instanceVariableNames: ''''
classVariableNames: ''''
category: ''' , systemCategory , ''''.
classDefinition := UIManager default
multiLineRequest: 'Edit class definition:'
initialAnswer: classDefinition
answerHeight: 150.
(classDefinition isNil or: [ classDefinition isEmpty ])
ifTrue: [ ^ Abort signal ].
self class compiler
source: classDefinition;
logged: true;
evaluate.
^ (node owningScope lookupVar: className)
ifNil: [self error: 'should be not happen']
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> defineTrait: traitName [
"Prompts the user to define a new trait."
| traitSymbol systemCategory traitDefinition |
traitSymbol := traitName asSymbol.
systemCategory := self methodClass category
ifNil: [ 'Unknown' ].
traitDefinition := 'Trait named: #' , traitSymbol , '
uses:{}
package: ''' , systemCategory , ''''.
traitDefinition := UIManager default
multiLineRequest: 'Edit trait definition:'
initialAnswer: traitDefinition
answerHeight: 150.
(traitDefinition isNil or: [ traitDefinition isEmpty ])
ifTrue: [ ^ Abort signal ].
self class compiler
source: traitDefinition;
logged: true;
evaluate.
^ (node owningScope lookupVar: traitSymbol)
ifNil: [self error: 'should be not happen']
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> lookUpVariable [
^ (node owningScope lookupVar: node name)
ifNil: [self error: 'should be found']
]
{ #category : #accessing }
OCUndeclaredVariableWarning >> node: aVariableNode [
super node: aVariableNode.
messageText := 'Undeclared temp: ', aVariableNode name.
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> openMenuIn: aBlock [
| alternatives labels actions lines caption choice name interval |
interval := node sourceInterval.
name := node name.
alternatives := self possibleVariablesFor: name.
labels := OrderedCollection new.
actions := OrderedCollection new.
lines := OrderedCollection new.
name first isLowercase
ifTrue: [
labels add: 'Declare new temporary variable'.
actions add: [ self declareTempAndPaste: name ].
labels add: 'Declare new instance variable'.
actions add: [ self declareInstVar: name ] ]
ifFalse: [
labels add: 'Leave variable undeclared'.
actions add: [ self declareUndefined ].
lines add: labels size.
labels add: 'Define new class'.
actions
add: [
[ self defineClass: name ]
on: Abort
do: [ self openMenuIn: aBlock ] ].
labels add: 'Declare new global'.
actions add: [ self declareGlobal ].
labels add: 'Declare new class variable'.
actions add: [ self declareClassVar ] .
labels add: 'Define new trait'.
actions
add: [
[ self defineTrait: name ]
on: Abort
do: [ self openMenuIn: aBlock ] ] ].
lines add: labels size.
alternatives
do: [ :each |
labels add: each.
actions
add: [
self substituteVariable: each atInterval: interval.
(ReparseAfterSourceEditing new newSource: self requestor text) signal ] ].
lines add: labels size.
labels add: 'Cancel'.
caption := 'Unknown variable: ' , name , ' please correct, or cancel:'.
choice := aBlock value: labels value: lines value: caption.
choice ifNotNil: [ self resume: (actions at: choice ifAbsent: [ compilationContext failBlock value ]) value ]
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> possibleVariablesFor: proposedVariable [
| results class |
class := node methodNode methodClass .
results := proposedVariable correctAgainst: node temporaryVariables
continuedFrom: nil.
proposedVariable first canBeGlobalVarInitial ifTrue:
[ results := class possibleVariablesFor: proposedVariable
continuedFrom: results ].
^ proposedVariable correctAgainst: nil continuedFrom: results.
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> substituteVariable: varName atInterval: anInterval [
self
substituteWord: varName
wordInterval: anInterval
offset: 0.
self methodNode source: self requestor text.
node replaceWith:((RBVariableNode named: varName) binding: (node owningScope lookupVar: varName)).
^ (node owningScope lookupVar: varName)
ifNil: [self error: 'should be found'].
]
{ #category : #correcting }
OCUndeclaredVariableWarning >> substituteWord: correctWord wordInterval: spot offset: o [
"Substitute the correctSelector into the (presuamed interactive) receiver."
self requestor correctFrom: (spot first + o)
to: (spot last + o)
with: correctWord.
^ o + correctWord size - spot size
]