OILS / test / ysh-runtime-errors.sh View on Github | oilshell.org

1145 lines, 458 significant
1#!/usr/bin/env bash
2#
3# Usage:
4# test/ysh-runtime-errors.sh <function name>
5
6set -o nounset
7set -o pipefail
8set -o errexit
9
10source test/common.sh
11source test/sh-assert.sh # _assert-sh-status
12
13#
14# Cases
15#
16
17test-no-typed-args() {
18 _ysh-error-X 2 'echo (42)'
19 _ysh-error-X 2 'echo { echo hi }'
20
21 # Hm write could be J8 notation? like json8 write (x)?
22 _ysh-error-X 2 'write (42)'
23
24 _ysh-error-X 2 'true (42)'
25 _ysh-error-X 2 'false { echo hi }'
26
27 _ysh-error-X 2 'test x (42)'
28 _ysh-error-X 2 'test x { echo hi }'
29}
30
31test-undefined-vars() {
32 _ysh-error-1 'echo hi; const y = 2 + x + 3'
33 _ysh-error-1 'if (x) { echo hello }'
34 _ysh-error-1 'if (${x}) { echo hi }'
35
36 # BareDecl and regex
37 _ysh-error-1 'const x = / @undef /; echo hi'
38
39 _ysh-error-1 'var x = undef; echo $x' # VarDecl
40 _ysh-error-1 'setvar a = undef' # PlaceMutation
41}
42
43test-word-eval-with-ysh-data() {
44 _ysh-expr-error 'var d = {}; echo ${d:-}'
45
46 _osh-error-X 3 'var d = {}; echo ${#d}'
47
48 _osh-error-X 3 'var d = {}; echo ${d[0]}'
49
50 _osh-error-X 3 'var d = {}; echo ${d[@]:1:3}'
51
52 _osh-error-X 3 'var d = {}; echo ${!d}'
53
54 _osh-error-X 3 'var d = {}; echo ${!d[@]}'
55
56 _osh-error-X 3 'var d = {}; echo ${d#prefix}'
57
58 _osh-error-X 3 'var d = {}; echo ${d//a/b}'
59
60}
61
62test-ysh-word-eval() {
63 # Wrong sigil
64 _ysh-expr-error 'echo $[maybe("foo")]'
65
66 # Wrong sigil
67 _ysh-expr-error 'source $LIB_YSH/math.ysh; echo $[identity({key: "val"})]'
68
69 # this should be consistent
70 _ysh-expr-error 'source $LIB_YSH/math.ysh; write -- @[identity([{key: "val"}])]'
71
72 _ysh-should-run 'var x = [1, 2]; write @x'
73
74 # errors in items
75 _ysh-expr-error 'var x = [3, {}]; write @x'
76
77 _ysh-expr-error 'var x = [3, {}]; write @[x]'
78
79 # errors at top level
80 _ysh-expr-error 'var x = /d+/; write @x'
81
82 _ysh-expr-error 'var x = /d+/; write @[x]'
83}
84
85# Continuation of above
86test-cannot-stringify-list() {
87 # List can't be stringified
88 _ysh-expr-error 'var mylist = [1,2,3]; write $mylist'
89 _ysh-expr-error 'var mylist = [1,2,3]; write $[mylist]'
90
91 _ysh-should-run '= str(/d+/)'
92
93 _ysh-expr-error '= str([1,2])'
94 _ysh-expr-error '= str({})'
95
96 # Not sure if I like this join() behavior
97 _ysh-should-run '= join([true, null])'
98
99 # Bad error
100 _ysh-expr-error '= join([[1,2], null])'
101}
102
103test-ysh-expr-eval() {
104 _ysh-expr-error 'echo $[42 / 0 ]'
105
106 _ysh-expr-error 'var d = {}; var item = d->nonexistent'
107
108 _ysh-expr-error 'var d = {}; var item = d["nonexistent"]'
109
110 _ysh-expr-error 'var a = []; setvar item = a[1]'
111
112 _ysh-expr-error 'const x = 42 / 0'
113
114 # command sub as part of expression retains its exit code
115 _ysh-error-1 'var x = "z" ++ $(false)'
116 #_ysh-error-1 'var x = "z" ++ $(exit 42)'
117
118 _ysh-expr-error 'case (42 / 0) { * { echo hi } }; echo OK'
119
120 _ysh-expr-error 'var d = {}; for x in $[d->zzz] { echo hi }'
121
122 # Wrong index type
123 _ysh-expr-error 'var d = {}; setvar d[42] = 3'
124 _ysh-expr-error 'var L = []; setvar L["key"] = 3'
125
126 # Index out of bounds
127 _ysh-expr-error 'var L = []; setvar L[99] = 3'
128 _ysh-expr-error 'var L = []; pp (L[-99])'
129}
130
131test-ysh-expr-eval-2() {
132 _ysh-expr-error 'var L = []; var slice = L["foo": "bar"]'
133
134 _ysh-expr-error '= 3 < true'
135 _ysh-expr-error '= "a" < "b"'
136
137 _ysh-expr-error 'var key = 42; var d = {[key]: 3}'
138
139 _ysh-expr-error 'var d = {}; var a = d.a'
140 _ysh-expr-error 'var d = []; var a = d.a'
141
142 _ysh-expr-error '= 3 ** -2'
143 _ysh-expr-error '= 3.2 ** 2'
144
145 _ysh-expr-error '= - "foo"'
146}
147
148test-user-reported() {
149 #_ysh-error-1 'echo'
150
151 # Issue #1118
152 # Some tests became test/parse-errors.sh
153
154
155 # len(INTEGER) causes the same problem
156 _ysh-expr-error '
157 var snippets = [{status: 42}]
158 for snippet in (snippets) {
159 if (len(42)) {
160 echo hi
161 }
162 }
163 '
164
165 # len(INTEGER) causes the same problem
166 _ysh-expr-error '
167 var count = 0
168
169 # The $ causes a weird error
170 while (count < len(count)) {
171 setvar count += 1
172 }
173 '
174}
175
176test-fallback-locations() {
177 # Melvin noticed bug here
178 _ysh-expr-error 'if (len(42)) { echo hi }'
179
180 # Be even more specific
181 _ysh-expr-error 'if (1 + len(42)) { echo hi }'
182
183 # From Aidan's PR -- redefinition
184 _ysh-error-1 'const f = 42; func f() { echo hi }'
185
186 # ForEach shell
187 _ysh-expr-error 'for x in $[2 + len(42)] { echo hi }'
188
189 # ForEach YSH
190 _ysh-expr-error 'for x in (len(42)) { echo hi }'
191
192 _ysh-expr-error 'while (len(42)) { echo hi }'
193
194 _ysh-expr-error 'case (len(42)) { pat { echo argument } }'
195 _ysh-expr-error 'case (42) { (len(42)) { echo arm } }'
196
197 _ysh-expr-error 'case "$[len(42)]" in pat) echo hi ;; esac'
198
199 _ysh-expr-error 'var x = 3 + len(42)'
200 _ysh-expr-error 'const x = 3 + len(42)'
201 _ysh-expr-error 'setvar x = 3 + len(42)'
202
203 _ysh-expr-error 'setvar x = "s" + 5'
204 _ysh-expr-error 'while ("s" + 5) { echo yes } '
205
206 #_ysh-expr-error 'func f(x) { return (x) }; var x = f([1,2])(3); echo $x'
207
208 # Really bad one
209 _ysh-expr-error 'func f(x) { return (x) }; var x = f([1,2])[1](3); echo $x'
210}
211
212test-more-locations() {
213 # Dict instead of Obj
214 # We need to call rd.BlamePos() right afterward
215 _ysh-expr-error \
216 'var Counter_methods = {}; var c = Object(Counter_methods, {i: 5})'
217
218 # This blames the ( after 'repeat' - that seems wrong
219 # Could clarify that it is Arg 1 to fromJson(), not repeat()
220 # - Or we could highlight MULTIPLE tokens, the whole repeat() call
221 # - Or nested calls fall back?
222
223# func repeat(x, y) { return (null) }; var x = fromJson(repeat(123, 20))
224 ^
225# [ -c flag ]:1: fatal: Arg 1 should be a Str, got Null
226
227
228 _ysh-expr-error \
229 'func repeat(x, y) { return (null) }; var x = fromJson(repeat('123', 20))'
230
231 # This blames 'error'
232 _ysh-expr-error \
233 'source $LIB_YSH/list.ysh; var x = fromJson(repeat('123', 20))'
234}
235
236test-EvalExpr-calls() {
237 ### Test everywhere expr_ev.EvalExpr() is invoked
238
239 _ysh-expr-error 'json write (len(42))'
240
241 _ysh-expr-error '= len(42)'
242 _ysh-expr-error 'call len(42)'
243
244 _ysh-expr-error 'echo $[len(42)]'
245 _ysh-expr-error 'echo $[len(z = 42)]'
246
247 _ysh-expr-error 'echo @[len(42)]'
248 _ysh-expr-error 'echo @[len(z = 42)]'
249
250 _ysh-expr-error 'const x = len(42)'
251 _ysh-expr-error 'setvar x += len(42)'
252
253 _ysh-expr-error '
254 var d = {}
255 setvar d[len(42)] = "foo"
256 '
257
258 _ysh-error-X 2 '
259 var d = {}
260 setvar len(42).z = "foo"
261 '
262
263 _ysh-expr-error '
264 hay define Package
265 Package foo {
266 x = len(42)
267 }
268 '
269
270 _ysh-expr-error 'if (len(42)) { echo hi }'
271
272 _ysh-expr-error 'while (len(42)) { echo hi }'
273
274 _ysh-expr-error 'for x in (len(42)) { echo $x }'
275}
276
277
278test-hay() {
279 _ysh-error-X 127 '
280hay define package user TASK
281
282hay eval :result {
283 package foo {
284 # commands can be run while evaluating
285 oops
286 }
287
288 bad 2
289}
290'
291}
292
293
294test-hay-osh() {
295 # forgot parse_brace
296 _osh-error-X 2 '
297hay define package TASK
298
299package foo {
300 version = 1
301}
302'
303
304 # forgot parse_equals
305 _osh-error-X 127 '
306shopt --set parse_brace
307
308hay define package TASK
309
310hay eval :result {
311 package foo {
312 version = 1
313 }
314}
315'
316}
317
318test-eggex() {
319 # forgot parse_brace
320 _ysh-should-run ' = / [ \x00 \xff ] /'
321 _ysh-should-run ' = / [ \x00-\xff ] /'
322
323 # Shouldn't be in strings
324
325 cat >_tmp/test-eggex.txt <<'EOF'
326= / [ $'\x00 \xff' ] /
327EOF
328
329 _ysh-error-1 "$(cat _tmp/test-eggex.txt)"
330
331 _ysh-should-run ' = / [ \u{0} ] /'
332 _ysh-should-run ' = / [ \u{0}-\u{1} ] /'
333
334 # Too high
335 _ysh-error-1 'var x =/ [ \u{80} ] /; echo $x'
336 _ysh-error-1 'var x = / [ \u{7f}-\u{80} ] /; echo $x'
337
338 # Now test special characters
339 _ysh-should-run "$(cat <<'EOF'
340= / [ \\ '^-]' 'abc' ] /
341EOF
342)"
343
344 # Special chars in ranges are disallowed for simplicity
345 _ysh-error-1 "var x = / [ a-'^' ] /; echo \$x"
346 _ysh-error-1 "var x = / [ '-'-z ] /; echo \$x"
347 _ysh-error-1 "var x = / [ ']'-z ] /; echo \$x"
348
349 # TODO: Disallow this. It translates to [^], which is a syntax error in
350 # egrep "Unmatched [ or [^"
351 _ysh-should-run "var x = / ['^'] /; echo \$x"
352
353 _ysh-expr-error '
354 var i = 42
355 = / @i / # splice object of wrong type
356 '
357
358 _ysh-expr-error '
359 var i = 42
360 = / [a @i] / # char class splice object of wrong type
361 '
362}
363
364test-eggex-2() {
365 _ysh-should-run "var sq = / 'foo'+ /"
366
367 _ysh-should-run "$(cat <<'EOF'
368 var sq = / ('foo')+ /
369 echo $sq
370
371 var sq2 = / <capture 'foo'>+ /
372 echo $sq2
373EOF
374)"
375
376 _ysh-error-1 '
377 var literal = "foo"
378 var svs = / @literal+ /
379 echo $svs
380 '
381}
382
383test-eggex-api() {
384 _ysh-expr-error '= _group(0)' # No groups
385
386 _ysh-expr-error 'if ("foo" ~ /[a-z]/) { echo $[_group(1)] }'
387 _ysh-expr-error 'if ("foo" ~ /[a-z]/) { echo $[_group("name")] }'
388
389 # ERE
390 _ysh-expr-error 'if ("foo" ~ "[a-z]") { echo $[_group(1)] }'
391 _ysh-expr-error 'if ("foo" ~ "[a-z]") { echo $[_group("name")] }'
392
393 _ysh-expr-error '= _group("foo")' # No such group
394}
395
396test-eggex-convert-func() {
397
398 _ysh-should-run '= / <capture d+ as month: int> /'
399 _ysh-should-run '= / <capture d+: int> /'
400 _ysh-should-run '= / <capture d+> /'
401
402 # bad convert func
403 _ysh-expr-error '= / <capture d+ as month: BAD> /'
404 _ysh-expr-error '= / <capture d+: BAD> /'
405
406 # type error calling convert func (evalExpr)
407 _ysh-expr-error 'var pat = / <capture d+: evalExpr> /; var m = "10" => search(pat) => group(1)'
408}
409
410test-int-convert() {
411 _ysh-expr-error '= int({})'
412 _ysh-expr-error '= int([])'
413 _ysh-expr-error '= int("foo")'
414 _ysh-expr-error '= int(len)'
415 _ysh-expr-error '= int("foo" => startsWith)'
416
417 _ysh-expr-error '= int(NAN)'
418 _ysh-expr-error '= int(-INFINITY)'
419}
420
421test-float-convert() {
422 _ysh-expr-error '= float({})'
423 _ysh-expr-error '= float([])'
424 _ysh-expr-error '= float("foo")'
425 _ysh-expr-error '= float(len)'
426 _ysh-expr-error '= float("foo"->startswith)'
427}
428
429test-str-convert() {
430 _ysh-expr-error '= str({})'
431 _ysh-expr-error '= str([])'
432 _ysh-expr-error '= str(len)'
433 _ysh-expr-error '= str("foo"->startswith)'
434}
435
436test-list-convert() {
437 _ysh-expr-error '= list(1)'
438 _ysh-expr-error '= list(len)'
439 _ysh-expr-error '= list("foo"->startswith)'
440}
441
442test-dict-convert() {
443 _ysh-expr-error '= dict(1)'
444 _ysh-expr-error '= dict("foo")'
445 _ysh-expr-error '= dict(len)'
446 _ysh-expr-error '= dict("foo"->startswith)'
447 _ysh-expr-error '= dict([["too", "many", "parts"]])'
448}
449
450test-proc-error-locs() {
451
452 # positional
453 _ysh-expr-error '
454 var d = [1]
455
456 func f(a=1, x=d[2]) {
457 echo hi
458 }
459 '
460
461 _ysh-expr-error '
462 var d = [1]
463
464 func f(; n=1, m=d[2]) {
465 echo hi
466 }
467 '
468}
469
470test-func-error-locs() {
471 # free funcs
472 _ysh-expr-error '= join(["foo", "bar"], " ", 99)' # too many args
473 _ysh-expr-error '= int()' # not enough args
474 _ysh-expr-error '= str({})' # wrong type
475
476 # bound funcs
477 _ysh-expr-error '= "foo"->startswith("f", "o")' # too many args
478 _ysh-expr-error '= "foo"->startswith()' # not enough args
479 _ysh-expr-error '= "foo"->startswith(1)' # wrong type
480
481 _ysh-expr-error '
482 func f(x) {
483 return (x)
484 }
485 = f()
486 '
487}
488
489test-attr-error-locs() {
490 _ysh-expr-error '= {}.key'
491 _ysh-expr-error '= {}->method'
492
493 _ysh-expr-error 'var obj = Object(null, {}); = obj.attr'
494 _ysh-expr-error 'var obj = Object(null, {}); = obj->method'
495
496}
497
498# TODO:
499test-error-loc-bugs() {
500 _ysh-expr-error '
501func id(x) {
502 return (x)
503}
504
505#pp test_ (id(len(42)))
506
507# This should point at ( in len, not id(
508pp test_ (len(id(42)))
509 '
510
511 _ysh-expr-error '
512var methods = {}
513
514# Should point at methods, not {}
515var o = Object(methods, {})
516 '
517}
518
519test-var-decl() {
520 _ysh-expr-error 'var x, y = 1, 2, 3'
521 _ysh-expr-error 'setvar x, y = 1, 2, 3'
522}
523
524test-const-decl() {
525 _ysh-error-1 'const x = {}; const x = {};'
526 _ysh-error-1 'const x; const x;'
527}
528
529test-proc-defaults() {
530
531 # should be string
532 _ysh-expr-error 'proc p(word=42) { echo }'
533 _ysh-expr-error 'proc p(word=null) { echo }'
534
535 # should be ^() or null
536 _ysh-expr-error 'proc p( ; ; ; block="str") { echo }'
537 _ysh-expr-error 'proc p( ; ; ; block=[]) { echo }'
538
539 _ysh-should-run 'proc p( ; ; ; block=^(echo hi)) { true }'
540 _ysh-should-run 'proc p( ; ; ; block=null) { true }'
541
542 # divide by zero
543 _ysh-expr-error 'proc p(word; t=42/0) { echo }'
544
545 _ysh-error-X 1 'proc p(word; t=f()) { echo }'
546
547 _ysh-error-X 1 'proc p(word; t=42; named=undef) { echo }'
548
549 _ysh-error-X 1 'proc p(word; t=42; named=43; block=ZZ) { echo }'
550
551 _ysh-should-run '
552 proc p(word="yo"; t=42; named=43; block=null) {
553 #echo $word $t $named $block
554 echo $word $t $block
555 }
556 p
557 '
558}
559
560test-proc-passing() {
561 # Too few words
562 _ysh-error-X 3 '
563 proc p(a, b) { echo }
564 p a
565 '
566
567 # Too many words
568 _ysh-error-X 3 '
569 proc p(a, b) { echo }
570 p AA b c DD
571 '
572
573 # Too few typed
574 _ysh-error-X 3 '
575 proc p( ; a, b) { echo }
576 p (42)
577 '
578
579 # Too many words
580 _ysh-error-X 3 '
581 proc p( ; a, b) { echo }
582 p (42, 43, 44, 45)
583 '
584
585 _ysh-expr-error '
586 proc p(; a, b) {
587 echo $a - $b -
588 }
589 p (...[1, 2])
590 p (...3)
591 '
592
593 # positional: rest args and spread
594 _ysh-should-run '
595 proc p(; a, ...b) {
596 echo $a - @b -
597 }
598 p (1, 2, 3)
599
600 var x = [4, 5, 6]
601 p (...x)
602 '
603
604 # named: splat
605 _ysh-should-run '
606 proc myproc (; p ; a, b) {
607 echo "$p ; $a $b"
608 }
609 var kwargs = {a: 42, b: 43}
610 myproc (99; ...kwargs)
611 '
612
613 # named: rest args
614 _ysh-should-run '
615 proc myproc (; p ; a, b, ...named) {
616 = p
617 = a
618 = b
619 = named
620 }
621 var kwargs = {a: 42, b: 43, c:44}
622 myproc (99; ...kwargs)
623 '
624}
625
626# TODO: improve locations for all of these
627test-proc-missing() {
628 # missing word param
629 _ysh-error-X 3 '
630 proc myproc (w) {
631 = w
632 }
633 myproc
634 '
635
636 # missing typed param
637 _ysh-error-X 3 '
638 proc myproc (w; t1, t2) {
639 = w
640 = t
641 }
642 myproc foo (42)
643 '
644
645 # missing named param
646 _ysh-error-X 3 '
647 proc myproc (; p ; a, b) {
648 echo "$p ; $a $b"
649 }
650 myproc (99, b=3)
651 '
652
653 # missing named param with semicolon
654 _ysh-error-X 3 '
655 proc myproc (; p ; a, b) {
656 echo "$p ; $a $b"
657 }
658 myproc (99; b=3)
659 '
660
661 # missing block param
662 _ysh-error-X 3 '
663 proc myproc (w; p ; a, b; block) {
664 = block
665 }
666 myproc foo (99, a=1, b=2)
667 '
668}
669
670test-proc-extra() {
671
672 # extra word
673 _ysh-error-X 3 '
674 proc myproc () {
675 echo hi
676 }
677 myproc foo
678 '
679
680 # extra positional
681 _ysh-error-X 3 '
682 proc myproc (w) {
683 echo hi
684 }
685 myproc foo (42)
686 '
687
688 # extra named
689 _ysh-error-X 3 '
690 proc myproc (w; p) {
691 echo hi
692 }
693 myproc foo (42; named=1)
694 '
695
696 # extra block. TODO: error is about typed args
697 _ysh-error-X 3 '
698 proc myproc (w; p; n) {
699 echo hi
700 }
701 myproc foo (42; n=1) { echo hi }
702 '
703}
704
705
706test-func-defaults() {
707 _ysh-error-X 1 'func f(a=ZZ) { echo }'
708 _ysh-error-X 1 'func f(a; named=YY) { echo }'
709
710 _ysh-expr-error 'func f(a=[]) { echo }'
711 _ysh-expr-error 'func f(; d={a:3}) { echo }'
712}
713
714test-func-missing() {
715 _ysh-expr-error '
716 func f(x, y) {
717 echo "$x $y"
718 }
719 call f(1)
720 '
721
722 _ysh-expr-error '
723 func f(x, y; z) {
724 echo "$x $y"
725 }
726 call f(3, 4)
727 '
728
729}
730
731test-func-extra() {
732 _ysh-expr-error '
733 func f() {
734 echo "$x $y"
735 }
736 call f(42) # extra pos
737 '
738
739 _ysh-expr-error '
740 func f() {
741 echo "$x $y"
742 }
743 call f(; x=32) # extra named
744 '
745}
746
747test-func-passing() {
748 # rest can't have default -- parse error
749 _ysh-error-X 2 '
750 func f(...rest=3) {
751 return (42)
752 }
753 '
754
755 _ysh-expr-error '
756 func f(a, b) {
757 echo "$a -- $b"
758 }
759 = f()
760 '
761
762 _ysh-expr-error '
763 func f(a, b) {
764 echo "$a -- $b"
765 }
766 = f(...[1, 2])
767 = f(...3)
768 '
769
770 # rest args and splat
771 _ysh-should-run '
772 func f(a, ...b) {
773 echo $a - @b -
774 }
775 = f(1, 2, 3)
776
777 var x = [4, 5, 6]
778 = f(...x)
779 '
780
781 # Named splat
782 _ysh-should-run '
783 func f(p ; a, b) {
784 echo "$p ; $a $b"
785 }
786 var kwargs = {a: 42, b: 43, c: 44}
787 = f(99; ...kwargs)
788 '
789}
790
791test-read-builtin() {
792 # no typed args
793 _ysh-error-X 2 'echo hi | read (&x)'
794 _ysh-error-X 2 'echo hi | read --all x y'
795 _ysh-error-X 2 'echo hi | read --line x y'
796}
797
798test-equality() {
799 _ysh-expr-error '
800 = ^[42] === ^[43]
801 '
802
803 _ysh-expr-error '
804 = ^(echo hi) === ^(echo yo)
805 '
806
807 return
808
809 # Hm it's kind of weird you can do this -- it's False
810 _ysh-expr-error '
811 = ^[42] === "hi"
812 '
813}
814
815test-float-equality() {
816 _ysh-expr-error '
817var x = 1
818pp test_ (42.0 === x)'
819
820 _ysh-expr-error 'pp test_ (2.0 === 1.0)'
821}
822
823test-place() {
824 _ysh-expr-error '
825 var a = null
826 var p = &a
827 call p->setValue() # 1 arg
828 '
829
830 _ysh-expr-error '
831 var a = null
832 var p = &a
833 call p->setValue(3, 4)
834 '
835
836 # DISABLED 2024-10, after implementing modules
837 if false; then
838 _ysh-error-1 '
839 func f() {
840 var s = "foo"
841 return (&s)
842
843 }
844 var p = f()
845 call p->setValue(3)
846 '
847 fi
848
849}
850
851test-json() {
852 _ysh-expr-error 'json write'
853 _ysh-expr-error 'json write (42, 43)'
854
855 _ysh-error-X 2 'json read zz'
856 _ysh-error-X 2 'json read yy zz'
857 _ysh-error-X 3 'json read (&x, 43)'
858}
859
860# For decoding errors, see data_lang/j8-errors.sh
861
862test-error-builtin() {
863
864 _ysh-error-X 2 'error '
865 _ysh-error-X 2 'error --'
866
867 # These are OK
868 _ysh-error-X 10 'error -- oops'
869 _ysh-error-X 10 'error oops'
870
871 _ysh-error-X 99 'error oops (code=99)'
872}
873
874test-fat-arrow() {
875 #_ysh-should-run '= "str" -> upper()'
876 _ysh-should-run '= "str" => upper()'
877
878 _ysh-expr-error '= "str" -> bad()'
879
880 # We get 'Undefined variable' error because of the fallback, could make it better
881 _ysh-error-X 1 '= "str" => bad()'
882
883 _ysh-should-run '= ["3", "4"] => join("/")'
884
885 # Good error message for method chaining
886 _ysh-expr-error '= "badstring" => join("/")'
887
888
889 # float has no ExactlyEqual
890 _ysh-error-X 3 "= [1.0, 2.0] => indexOf(3.14)"
891
892 # Invalid type
893 _ysh-expr-error '
894 var myint = 42
895 = "badstring" => myint("/")
896 '
897}
898
899test-method-type-errors() {
900 _ysh-expr-error '= "hi" => search(42)'
901 _ysh-expr-error '= "hi" => leftMatch(42)'
902 _ysh-expr-error "var m = 'hi' => leftMatch(/'hi'/); = m => group(3.14)"
903}
904
905test-str-replace() {
906 # Some ad hoc tests - spec tests cover this
907 if false; then
908 _ysh-should-run '= "hi" => replace("i", "b")'
909 _ysh-should-run '= "hi" => replace(/[a-z]/, "b")'
910 _ysh-should-run '= "hi" => replace(/[a-z]/, "b", count=1)'
911 _ysh-should-run '= "foo42" => replace(/<capture d+>/, ^"hi $1")'
912 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi $num")'
913 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi ${num}")'
914 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi $[num]")'
915 # test out globals - is this desirable?
916 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^["hi $[num] $PATH"])'
917 # -1 is replace all
918 _ysh-should-run '= "foo" => replace("o", "x", count=-1)'
919 _ysh-should-run '= "foo" => replace("o", "x", count=-2)'
920 fi
921 # Replace empty string? Weird Python behavior
922 _ysh-should-run '= "foo" => replace("", "-")'
923 _ysh-should-run '= "foo" => replace("", "-", count=2)'
924
925 # Use Expr with string
926 _ysh-should-run '= "foo" => replace("o", ^"-")'
927 # $0 is regular $0 here
928 _ysh-should-run '= "foo" => replace("o", ^"-$0")'
929
930 # Hm $0 isn't set?
931 _ysh-should-run '= "foo" => replace(/[o]/, ^"-$0")'
932 # Here $1 is set
933 _ysh-should-run '= "foo" => replace(/<capture [o]>/, ^"-$1")'
934 _ysh-should-run '= "foo" => replace(/<capture [o] as letter>/, ^"-$letter")'
935
936 # Invalid arguments
937 _ysh-expr-error '= "foo" => replace(42, "x")'
938 _ysh-expr-error '= "foo" => replace("x", 42)'
939
940 # Invalid evaluation
941 _ysh-expr-error '= "foo" => replace("x", ^[42])'
942}
943
944test-remainder() {
945 # second number can't be negative
946 _ysh-expr-error '= 5 % -3'
947 _ysh-expr-error 'var x = 5; setvar x %= -3'
948}
949
950test-append-usage-error() {
951 _ysh-should-run 'append x ([])'
952
953 _ysh-expr-error 'append'
954
955 _ysh-expr-error 'append x' # Too few
956
957 _ysh-expr-error 'append x ([], [])' # Too many
958}
959
960test-try-usage-error() {
961 _ysh-error-X 2 '
962var s = "README"
963case (s) {
964 README { echo hi }
965}
966echo hi
967
968try myproc
969if (_status !== 0) {
970 echo failed
971}
972'
973}
974
975test-trim-utf8-error() {
976 _ysh-error-here-X 3 << 'EOF'
977 var badUtf = b'\yF9'
978
979 # error is missed
980 call " a$[badUtf]b " => trim()
981 echo status=$_status
982
983 # error is found
984 call "$[badUtf]b " => trim()
985EOF
986}
987
988test-setglobal() {
989 _ysh-should-run '
990var a = [0]
991setglobal a[1-1] = 42
992pp test_ (a)
993 '
994
995 _ysh-expr-error '
996var a = [0]
997setglobal a[a.bad] = 42
998pp test_ (a)
999 '
1000
1001 _ysh-should-run '
1002var d = {e:{f:0}}
1003setglobal d.e.f = 42
1004pp test_ (d)
1005setglobal d.e.f += 1
1006pp test_ (d)
1007 '
1008}
1009
1010test-assert() {
1011 _ysh-expr-error 'assert [0.0]'
1012 _ysh-expr-error 'assert [3 > 4]'
1013
1014 _ysh-expr-error 'assert (0)'
1015 _ysh-expr-error 'assert (null === 42)'
1016
1017 _ysh-expr-error 'assert [null === 42]'
1018
1019 # One is long
1020 _ysh-expr-error 'assert [null === list(1 ..< 50)]'
1021
1022 # Both are long
1023 _ysh-expr-error 'assert [{k: list(3 ..< 40)} === list(1 ..< 50)]'
1024}
1025
1026test-pp() {
1027 _ysh-expr-error 'pp (42/0)'
1028
1029 # Multiple lines
1030 _ysh-should-run 'pp [42
1031/0]'
1032
1033 _ysh-expr-error 'pp [5, 6]'
1034
1035 _ysh-should-run 'pp (42)'
1036 _ysh-should-run 'var x = 42; pp (x)'
1037 _ysh-should-run '
1038var x = 42;
1039pp [x]'
1040
1041 _ysh-should-run '
1042var x = list(1 ..< 50);
1043pp [x]'
1044}
1045
1046test-module() {
1047 # no args
1048 _ysh-error-X 2 'use spec/testdata/module2/util.ysh; util'
1049
1050 # bad arg
1051 _ysh-error-X 2 'use spec/testdata/module2/util.ysh; util zz'
1052
1053 # proc with bad args
1054 _ysh-error-X 3 'use spec/testdata/module2/util2.ysh; util2 echo-args'
1055
1056 # malformed Obj
1057 _ysh-error-X 3 'use spec/testdata/module2/util2.ysh; util2 badObj otherproc'
1058}
1059
1060test-required-blocks() {
1061
1062 # These are procs, which normally give usage errors
1063 # The usage error prints the builtin name
1064 #
1065 # Funcs give you type errors though? Is that inconsistent?
1066
1067 _ysh-error-X 2 'shvar'
1068 _ysh-error-X 2 'push-registers'
1069
1070 _ysh-error-X 2 'redir'
1071 _ysh-error-X 2 'redir (42)'
1072 _ysh-error-X 2 'hay eval :myvar'
1073 _ysh-error-X 2 'hay eval :myvar (42)'
1074 _ysh-error-X 2 'try'
1075 _ysh-error-X 2 'ctx push ({})'
1076
1077 _ysh-error-X 2 'fork'
1078 _ysh-error-X 2 'forkwait'
1079
1080 # OK this is a type error
1081 _ysh-error-X 3 'forkwait ( ; ; 42)'
1082
1083 _ysh-error-X 2 'haynode Foo'
1084
1085 # Hm this isn't a usage error
1086 _ysh-error-X 3 'haynode Foo (42)'
1087
1088 # This neither
1089 _ysh-error-X 3 'haynode Foo ( ; ; 42)'
1090
1091 _ysh-should-run 'haynode Foo a { echo hi }'
1092}
1093
1094test-obj-methods() {
1095 _ysh-error-X 3 'var o = Object(null, {}); pp test_ (o[1])'
1096 _ysh-error-X 3 'var o = Str; pp test_ (Str[1])'
1097
1098 _ysh-error-X 3 'pp test_ (Bool[Bool])'
1099 _ysh-error-X 3 'pp test_ (Dict[Bool])'
1100 _ysh-error-X 3 'pp test_ (List[Str, Bool])'
1101
1102 # break invariants
1103 _ysh-error-X 3 'call propView(List)->erase("name"); pp test_ (List[Str])'
1104 _ysh-error-X 3 'call propView(Str)->erase("name"); pp test_ (List[Str])'
1105
1106 _ysh-error-X 3 'pp test_ (List[Str, 3])'
1107}
1108
1109test-int-overflow() {
1110 local pos='18446744073709551616'
1111 local neg='-18446744073709551616'
1112
1113 # arithmetic
1114
1115 # _ConvertToInt
1116 _ysh-error-1 "var s = '$pos'; = s % 2"
1117 _ysh-error-1 "var s = '$neg'; = s % 2"
1118
1119 # _ConvertToNumber
1120 _ysh-error-1 "var s = '$pos'; = s + 1"
1121 _ysh-error-1 "var s = '$neg'; = s + 1"
1122
1123 _ysh-error-1 "= '$pos' ~== 42"
1124 _ysh-error-1 "= '$neg' ~== 42"
1125
1126 # builtins
1127 _ysh-expr-error "= int('$pos')"
1128 _ysh-expr-error "= int('$neg')"
1129}
1130
1131soil-run-py() {
1132 run-test-funcs
1133}
1134
1135soil-run-cpp() {
1136 local ysh=_bin/cxx-asan/ysh
1137 ninja $ysh
1138 YSH=$ysh run-test-funcs
1139}
1140
1141run-for-release() {
1142 run-other-suite-for-release ysh-runtime-errors run-test-funcs
1143}
1144
1145"$@"