Skip to content

Commit aeed2b2

Browse files
author
Andy C
committed
[spec/ysh-user-feedback] Failing test cases for feedback related to static var and setvar
From Julian, Aidan, Machine Stops OK we should get rid of those static checks. [demo] Test out the equivalent in JavaScript Funny thing: I found a corner of JavaScript where you can either mutate or redeclare the loop variable, with 'let'. I thought that you would not be allowed to redeclare it - because there should be a single block/scope for the loop body? But it appears there are two? More JavaScript oddities.
1 parent 18ddc70 commit aeed2b2

File tree

2 files changed

+276
-15
lines changed

2 files changed

+276
-15
lines changed

demo/survey-static-names.sh

+204-14
Original file line numberDiff line numberDiff line change
@@ -84,42 +84,232 @@ js-var() {
8484
}
8585
'
8686

87-
banner 'STRICT UNDEF'
87+
banner 'USE BEFORE LET'
88+
nodejs -e '
89+
function outer() {
90+
// fails dynamically with ReferenceError
91+
console.log(`use before let: x=${x}`);
92+
let x = "foo";
93+
}
94+
95+
outer();
96+
'
97+
98+
echo
99+
}
100+
101+
control-flow() {
102+
banner 'BREAK'
103+
104+
# this is a syntax error! OK good
105+
nodejs -e '
106+
function f() {
107+
break;
108+
}
109+
'
110+
}
111+
112+
use-strict-static() {
113+
set +o errexit
114+
115+
banner 'DUPE PARAMS'
88116
nodejs -e '
89117
"use strict";
118+
function f(a, a) {
119+
return 42;
120+
}
121+
'
90122

91-
function outer() {
92-
let y = x + 1
93-
console.log(`x=${x}`);
123+
banner 'OCTAL'
124+
nodejs -e '
125+
"use strict";
126+
function f(a) {
127+
return 0123;
94128
}
95129
'
96130

97-
banner 'STRICT MUTATE'
131+
banner 'WITH'
132+
98133
nodejs -e '
134+
"use strict";
135+
function f() {
136+
with (x) {
137+
console.log(x);
138+
}
139+
}
140+
'
99141

100-
// Use strict prevents global mutation! But only at runtime
142+
banner 'OBJECT KEYS'
143+
144+
# Claude AI hallucinated this duplicate object keys, and then corrected itself
145+
nodejs -e '
146+
"use strict";
147+
function f() {
148+
return {a:1, a:2};
149+
}
150+
'
151+
}
152+
153+
branches() {
154+
set +o errexit
155+
156+
# spec/ysh-user-feedback - Julian
157+
158+
# JavaScript allows this because it's block scoped
159+
banner 'IF'
160+
nodejs -e '
161+
function f(x) {
162+
if (x === 2) {
163+
let tmp = "hello"
164+
} else {
165+
let tmp = "world"
166+
// This is an error
167+
// let tmp = "z"
168+
}
169+
//console.log(tmp);
170+
}
171+
172+
f(1);
173+
f(2);
174+
'
175+
176+
banner 'SWITCH'
177+
nodejs -e '
178+
function f(x) {
179+
switch (x) {
180+
case 1:
181+
let tmp = "hello"
182+
break;
183+
case 2:
184+
let tmp = "world"
185+
break;
186+
}
187+
}
188+
189+
f(1);
190+
f(2);
191+
'
192+
193+
banner 'SWITCH BLOCK'
194+
nodejs -e '
195+
function f(x) {
196+
switch (x) {
197+
case 1: {
198+
let tmp = "hello"
199+
console.log(tmp);
200+
break;
201+
}
202+
case 2: {
203+
let tmp = "world"
204+
console.log(tmp);
205+
break;
206+
}
207+
}
208+
}
209+
210+
f(1);
211+
f(2);
212+
'
213+
}
214+
215+
loop() {
216+
banner 'MODIFY FOR'
217+
nodejs -e '
218+
function f() {
219+
for (let x of [1, 2]) {
220+
console.log(`x = ${x}`);
221+
x = 3;
222+
console.log(x);
223+
}
224+
}
225+
226+
f();
227+
'
228+
229+
# Hm why is this allowed?
230+
banner 'LET LET'
231+
nodejs -e '
232+
function f() {
233+
for (let x of [1, 2]) {
234+
// console.log(`x = ${x}`);
235+
let x = 3;
236+
console.log(x);
237+
}
238+
}
239+
240+
f();
241+
'
242+
# Claude AI claims that there are two nested scopes, but I'm not so sure
243+
# It seemed to enter an infinite loop where the code analysis didn't agree
244+
# with it
245+
246+
# It also refers to "loop initialization scope" and "loop body scope"
247+
248+
# Another attempt:
249+
250+
# "What the specification actually describes is more precise and technical.
251+
# For a for...of loop like for (let x of [1,2]) { let x = 3 }, the ECMAScript
252+
# spec (as of ES2022) describes the behavior using concepts like:
253+
254+
# "Per-iteration binding instantiation - Each iteration of the loop creates a
255+
# new lexical environment for the loop variable
256+
#
257+
# "Block scoping - The {} of the loop body creates its own lexical environment
258+
259+
# "According to the specification, for loops with let declarations create a
260+
# fresh binding (variable) for each iteration of the loop. The loop body then
261+
# creates another lexical environment (scope) where another binding with the
262+
# same name can exist independently.
263+
264+
# "The precise section in the ECMAScript spec that addresses this is
265+
# typically found in sections covering "for statement" execution semantics.
266+
# The loop iteration variable and the loop body variable are in different
267+
# lexical environments in the specification's terminology, rather than
268+
# different "scopes" as I informally described.
269+
270+
banner 'LET x y'
271+
nodejs -e '
272+
273+
// Uh this is weird too, y = 1?
274+
function f() {
275+
for (let x = 0, y = x + 1; x < 5; ++x) {
276+
console.log(`x = ${x}, y = ${y}`);
277+
//let x = 3;
278+
}
279+
}
280+
281+
f();
282+
'
283+
}
284+
285+
286+
use-strict-dynamic() {
287+
set +o errexit
288+
289+
banner 'STRICT UNDEF'
290+
nodejs -e '
101291
"use strict";
102292
103293
function outer() {
104-
x = "mutate"
294+
let y = x + 1; // ReferenceError
295+
console.log(`x=${x}`);
105296
}
106297
107298
outer();
108-
console.log(`was global created? x=${x}`);
109299
'
110300

111-
banner 'USE BEFORE LET'
301+
banner 'STRICT MUTATE'
112302
nodejs -e '
303+
// Use strict prevents global mutation! But only at runtime
304+
"use strict";
305+
113306
function outer() {
114-
// fails dynamically with ReferenceError
115-
console.log(`use before let: x=${x}`);
116-
let x = "foo";
307+
x = "mutate"
117308
}
118309
119310
outer();
311+
console.log(`was global created? x=${x}`);
120312
'
121-
122-
echo
123313
}
124314

125315
"$@"

spec/ysh-user-feedback.test.sh

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## our_shell: ysh
2+
## oils_failures_allowed: 3
23

34
#### !== operator
45
var a = 'bar'
@@ -126,7 +127,7 @@ json write (e)
126127
## END
127128

128129
#### Invalid op on string
129-
shopt -s oil:all
130+
shopt -s ysh:all
130131

131132
var clients = {'email': 'foo', 'e2': 'bar'}
132133
for c in (clients) {
@@ -139,3 +140,73 @@ for c in (clients) {
139140
## STDOUT:
140141
email
141142
## END
143+
144+
145+
#### var in both branches of if (Julian)
146+
147+
# remove static check?
148+
149+
proc scopetest(; var) {
150+
if (var === 2) {
151+
var tmp = "hello"
152+
write $tmp
153+
} else {
154+
var tmp = "world"
155+
write $tmp
156+
}
157+
}
158+
159+
scopetest (1)
160+
scopetest (2)
161+
162+
## STDOUT:
163+
## END
164+
165+
#### var in branches of case (Aidan)
166+
shopt -s ysh:all
167+
168+
# at top level, there is no static check
169+
case (1) {
170+
(1) { var name = "one" }
171+
(2) { var name = "two" }
172+
}
173+
echo name=$name
174+
175+
proc my-proc {
176+
case (1) {
177+
(1) { var name = "one" }
178+
(2) { var name = "two" }
179+
}
180+
}
181+
182+
## STDOUT:
183+
## END
184+
185+
#### Modify for loop variable with var or setvar? (Machine Stops)
186+
187+
proc do-var {
188+
for x in a b {
189+
echo $x
190+
var x = 'zz'
191+
echo $x
192+
}
193+
}
194+
195+
proc do-setvar {
196+
for x in a b {
197+
echo $x
198+
setvar x = 'zz'
199+
echo $x
200+
}
201+
}
202+
203+
do-var
204+
echo
205+
do-setvar
206+
207+
## STDOUT:
208+
a
209+
zz
210+
b
211+
zz
212+
## END

0 commit comments

Comments
 (0)