Add a bunch of tests for compiler recovery
Ideally, any time you write: (local x (<some form with compiler error>)) (print x) I don't want it to say "unknown variable x". To make sure that's the case, I need to do as much error recovery as possible. I don't have full error recovery, but I've got the common cases, and I'm working down the fennel.friends list
This commit is contained in:
parent
3bc530e11b
commit
59b966a25f
@ -232,25 +232,41 @@ identifiers are declared / referenced in which places."
|
||||
(where [sym] (multisym? sym) (: (tostring sym) :find ":"))
|
||||
(reference sym scope :read)))
|
||||
|
||||
(local defer [])
|
||||
|
||||
(λ attempt-to-recover! [msg ?ast]
|
||||
(or (= 1 (msg:find "unknown identifier"))
|
||||
(= 1 (msg:find "local %S+ was overshadowed by a special form or macro"))
|
||||
(= 1 (msg:find "expected var "))
|
||||
(= 1 (msg:find "cannot call literal value"))
|
||||
(= 1 (msg:find "unexpected vararg"))
|
||||
(= 1 (msg:find "expected closing delimiter"))
|
||||
(= 1 (msg:find "expected body expression"))
|
||||
(= 1 (msg:find ".*fennel/macros.fnl:%d+: expected body"))
|
||||
(= 1 (msg:find "expected condition and body"))
|
||||
(= 1 (msg:find "expected whitespace before opening delimiter"))
|
||||
(= 1 (msg:find "malformed multisym"))
|
||||
(= 1 (msg:find "expected at least one pattern/body pair"))
|
||||
(= 1 (msg:find "module not found"))
|
||||
(= 1 (msg:find "expected even number of values in table literal"))
|
||||
(= 1 (msg:find "use $%.%.%. in hashfn"))
|
||||
(when (and (= 1 (msg:find "expected even number of name/value bindings"))
|
||||
(sequence? ?ast)
|
||||
(= 1 (% (length ?ast) 2)))
|
||||
(table.insert ?ast (sym :nil))
|
||||
(table.insert defer #(table.remove ?ast))
|
||||
true)
|
||||
(when (and (= 1 (msg:find "expected a function, macro, or special to call"))
|
||||
(list? ?ast)
|
||||
(= (length ?ast) 0))
|
||||
(table.insert ?ast (sym :do))
|
||||
true)))
|
||||
(table.insert defer #(table.remove ?ast))
|
||||
true)
|
||||
(when (= 1 (msg:find "unexpected multi symbol"))
|
||||
(let [old (tostring ?ast)]
|
||||
(tset ?ast 1 "!!invalid-multi-symbol!!")
|
||||
(table.insert defer #(tset ?ast 1 old))
|
||||
true))))
|
||||
|
||||
|
||||
(λ on-compile-error [_ msg ast call-me-to-reset-the-compiler]
|
||||
@ -353,6 +369,9 @@ identifiers are declared / referenced in which places."
|
||||
|
||||
(set fennel.macro-path old-macro-path))
|
||||
|
||||
(each [_ cmd (ipairs defer)]
|
||||
(cmd))
|
||||
|
||||
(set file.ast ast)
|
||||
(set file.calls calls)
|
||||
(set file.lexical lexical)
|
||||
|
||||
@ -143,11 +143,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
?parent (. parents 1)
|
||||
result []
|
||||
in-call-position? (and (fennel.list? ?parent)
|
||||
(or (= ?symbol (. ?parent 1))
|
||||
;; so, this is unfortunate
|
||||
(and (= ?symbol nil)
|
||||
(fennel.sym? (. ?parent 1) :do)
|
||||
(= (. ?parent 1 :bytestart) nil))))]
|
||||
(= ?symbol (. ?parent 1)))]
|
||||
(collect-scope scope :manglings #(doto (make-completion-item self file $ scope) (tset :kind kinds.Variable)) result)
|
||||
|
||||
(when in-call-position?
|
||||
|
||||
@ -57,23 +57,83 @@
|
||||
nil)
|
||||
|
||||
(fn test-multiple-errors []
|
||||
(check "(unknown-global-1 unknown-global-2)"
|
||||
[{:message "unknown identifier: unknown-global-1"}
|
||||
{:message "unknown identifier: unknown-global-2"}] [])
|
||||
(check "(let [x unknown-global"
|
||||
;; compiler recovery for every fennel.friend error
|
||||
|
||||
(check "(let [x.y.z 10]
|
||||
(print +))"
|
||||
[{:message "unexpected multi symbol x.y.z"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
;; use of global .* is aliased by a local
|
||||
|
||||
(check "(let [+ 10]
|
||||
(print +))"
|
||||
[{:message "local + was overshadowed by a special form or macro"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x 10]
|
||||
(set x 20)
|
||||
(print x +))"
|
||||
[{:message "expected var x"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
;; expected macros to be table
|
||||
;; expected each macro to be a function
|
||||
;; macro tried to bind .* without gensym
|
||||
|
||||
(check "(do unknown-global
|
||||
(print +))"
|
||||
[{:message "unknown identifier: unknown-global"}
|
||||
{:message "expected body expression"}
|
||||
{:message "expected closing delimiters )]"}] [])
|
||||
;; recovers from ()
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x ()]
|
||||
(print x +))"
|
||||
[{:message "expected a function, macro, or special to call"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x (3)]
|
||||
(print x +))"
|
||||
[{:message "cannot call literal value 3"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x (fn [] ...)]
|
||||
(print x +))"
|
||||
[{:message "unexpected vararg"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x #...]
|
||||
(print x +))"
|
||||
[{:message "use $... in hashfn"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
|
||||
(check "(let [x unknown-global"
|
||||
[{:message "unknown identifier: unknown-global"}
|
||||
{:message "expected body expression"}
|
||||
{:message "expected closing delimiters )]"}] [])
|
||||
;; recovers from mismatched let
|
||||
(check "(let [x]
|
||||
(print x +))"
|
||||
[{:message "expected even number of name/value bindings"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
;; recovers from missing condition (if)
|
||||
(check "(let [x (if)]
|
||||
(print x +))"
|
||||
[{:message "expected condition and body"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
; recovers from missing condition (when)
|
||||
(check "(let [x (when)]
|
||||
(print x +))"
|
||||
[{:message #($:find ".*macros.fnl:%d+: expected body")}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
;; recovers from missing body (when)
|
||||
(check "(let [x (when (< (+ 9 10) 21))]
|
||||
(print x +))"
|
||||
[{:message #($:find ".*macros.fnl:%d+: expected body")}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
(check "(let [x {:mismatched :curly :braces}]
|
||||
(print x +))"
|
||||
[{:message "expected even number of values in table literal"}
|
||||
{:message "tried to reference a special form without calling it"}] [])
|
||||
nil)
|
||||
|
||||
{: test-compile-error
|
||||
|
||||
Loading…
Reference in New Issue
Block a user