~technomancy/fennel

ref: 3ffd391d7fa3e774056555abd53156bb535d0c39 fennel/test/failures.fnl -rw-r--r-- 6.3 KiB
3ffd391dPhil Hagelberg Use semicolon instead of do/end as paren disambiguation. 1 year, 1 month ago
                                                                                
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
(local l (require :test.luaunit))
(local fennel (require :fennel))

(local failures {
  "\n\n(+))" "unknown:3: unexpected closing delimiter )"
  "\n\n(let [f (lambda []\n(local))] (f))" "4: expected name and value"
  "\n\n(let [x.y 9] nil)" "unknown:3: unexpected multi"
  "\n(when)" "Compile error in 'when' unknown:2"
  "((fn [] (require-macros \"test.macros\") (global x1 (->1 99 (+ 31)))))
      (->1 23 (+ 1))" "unknown global in strict mode"
  "()" "expected a function, macro, or special"
  "(789)" "cannot call literal value"
  "(do\n\n\n(each \n[x (pairs {})] (when)))" "when' unknown:5:"
  "(do\n\n\n(each \n[x 34 (pairs {})] 21))" "5: unable to bind number 34"
  "(each [k v (pairs {})] (BAD k v))" "BAD"
  "(f" "unknown:1: expected closing delimiter )"
  "(fn [12])" "expected symbol for function parameter"
  "(fn [:huh] 4)" "expected symbol for function parameter"
  "(fn []\n(for [32 34 32] 21))" "2: unable to bind number"
  "(fn [] [...])" "unexpected vararg"
  "(fn [false] 4)" "expected symbol for function parameter"
  "(fn [nil] 4)" "expected symbol for function parameter"
  "(fn global [] 1)" "overshadowed"
  "(fn global-caller [] (hey))" "unknown global"
  "(fn)" "expected parameters"
  "(global + 1)" "overshadowed"
  "(global - 1)" "overshadowed"
  "(global // 1)" "overshadowed"
  "(global 48 :forty-eight)" "unable to bind number 48"
  "(global good (fn [] nil)) (good) (BAD)" "BAD"
  "(global let 1)" "overshadowed"
  "(hey)" "unknown global"
  "(lambda x)" "expected arg list"
  "(let [:x 1] 1)" "unable to bind"
  "(let [[a & c d] [1 2]] c)" "rest argument before last parameter"
  "(let [b 9\nq (.)] q)" "2: expected table argument"
  "(let [bl 8 a bcd] nil)" "unknown global"
  "(let [false 1] 9)" "unable to bind boolean false"
  "(let [global 1] 1)" "overshadowed"
  "(let [next #(next $)] print)" "aliased by a local"
  "(let [nil 1] 9)" "unable to bind"
  "(let [pairs #(pairs $)] pairs)" "aliased by a local"
  "(let [t []] (set t.47 :forty-seven))" "can't start multisym segment with a digit: t.47"
  "(let [t []] (set t.:x :y))" "malformed multisym: t.:x"
  "(let [t []] (set t:.x :y))" "malformed multisym: t:.x"
  "(let [t []] (set t::x :y))" "malformed multisym: t::x"
  "(let [t {:a 1}] (+ t.a BAD))" "BAD"
  "(let [x 1 y] 8)" "expected even number of name/value bindings"
  "(let [x 1] (set-forcibly! x 2) (set x 3) x)" "expected var"
  "(let [x 1])" "expected body"
  "(let [x {:foo (fn [self] self.bar) :bar :baz}] x:foo)" "multisym method calls may only be in call position"
  "(let [x {:y {:foo (fn [self] self.bar) :bar :baz}}] x:y:foo)" "method must be last component of multisym: x:y:foo"
  "(local 47 :forty-seven)" "unable to bind number 47"
  "(local a-b 1) (global [a_b] [2])" "global a_b conflicts with local"
  "(local a-b 1) (global a_b 2)" "global a_b conflicts with local"
  "(local a~b 3)" "illegal character: ~"
  "(local ipairs #(ipairs $))" "aliased by a local"
  "(macros {:m (fn [t] `(each [mykey (pairs ,t)] (print mykey)))}) (m [])" "tried to bind mykey without gensym"
  "(macros {:m (fn [t] `(fn [xabc] (+ xabc 9)))}) ((m 4))" "tried to bind xabc without gensym"
  "(macros {:m (fn [y] `(let [x 1] (+ x ,y)))}) (m 4)" "tried to bind x without gensym"
  "(match [1 2 3] [a & b c] nil)" "rest argument before last parameter"
  "(not true false)" "expected one argument"
  "(print @)" "illegal character: @"
  ;; TODO: this should be an error in 1.0
  ;; "(local abc&d 19)" "illegal character: &"
  "(set [a b c] [1 2 3]) (+ a b c)" "expected local"
  "(set a 19)" "error in 'a' unknown:1: expected local"
  "(set)" "Compile error in 'set' unknown:1: expected name and value"
  "(x(y))" "expected whitespace before opening delimiter ("
  "(x[1 2])" "expected whitespace before opening delimiter ["
  "(fn abc:def [x] (+ x 2))" "unexpected multi symbol abc:def"
  "(macros {:foo {:bar (fn [] `(print :test))}})" "expected each macro to be function"
  "(macro m [] (getmetatable :foo)) (m)" "Illegal metatable"
  "(import-macros test :test.macros) (test.asdf)" "macro not found in imported macro module"
  "(import-macros {: asdf} :test.macros)" "macro asdf not found in module test.macros"
  "(with-open [(x y z) (values 1 2 3)])" "with-open only allows symbols in bindings"
  "#[$ $...] 1 2 3" "$ and $... in hashfn are mutually exclusive"
  "(eval-compiler (assert-compile false \"oh no\" 123))" "oh no"
  "(partial)" "expected a function"
  "(#)" "expected one argument"
})

(fn test-failures []
  (each [code expected-msg (pairs failures)]
    (let [(ok? msg) (pcall fennel.compileString code
                           {:allowedGlobals ["pairs" "next" "ipairs"]
                            :unfriendly true})]
      (l.assertFalse ok? (.. "Expected compiling " code " to fail."))
      (l.assertStrContains msg expected-msg))))

(fn test-macro []
  (let [code "(import-macros {: fail-one} :test.macros) (fail-one 1)"
        (ok? msg) (pcall fennel.compileString code)]
    (l.assertStrContains msg "test/macros.fnl:2: oh no")
    ;; sometimes it's "in function f" and sometimes "in upvalue f"
    (l.assertStrMatches msg ".*test/macros.fnl:2: in %w+ 'def'.*")
    (l.assertStrMatches msg ".*test/macros.fnl:6: in %w+ 'abc'.*")))

;; automated tests for suggestions are rudimentary because the usefulness of the
;; output is so subjective. to see a full catalog of suggestions, run the script
;; test/bad/friendly.sh and review that output.
(fn test-suggestions []
  (let [(_ msg) (pcall fennel.dofile "test/bad/set-local.fnl")
        (_ parse-msg) (pcall fennel.dofile "test/bad/odd-table.fnl")
        (_ assert-msg) (pcall fennel.eval
                              "(eval-compiler (assert-compile nil \"bad\" 1))")
        (_ msg4) (pcall fennel.eval "(abc] ;; msg4")
        (_ msg5) (pcall fennel.eval "(let) ;; msg5")]
    ;; show the raw error message
    (l.assertStrContains msg "expected var x")
    ;; offer suggestions
    (l.assertStrContains msg "Try declaring x using var")
    ;; show the code and point out the identifier at fault
    (l.assertStrContains msg "(set x 3)")
    (l.assertStrContains msg "\n     ^")
    ;; parse error
    (l.assertStrContains parse-msg "{:a 1 :b 2 :c}")
    ;; non-table AST in assertion
    (l.assertStrContains assert-msg "bad")
    ;; source should be part of the error message
    (l.assertStrContains msg4 "msg4")
    (l.assertStrContains msg5 "msg5")))

{: test-failures : test-suggestions : test-macro}