Add diagnostic for var-never-set.

This uses the isvar field from the opts map of the destructure hook.
This commit is contained in:
Phil Hagelberg 2023-12-05 21:09:04 -08:00 committed by XeroOl
parent f25fd9fd60
commit 5055fd31ef
4 changed files with 38 additions and 11 deletions

View File

@ -83,7 +83,7 @@ later by fennel-ls.language to answer requests from the client."
(when (or ?reference? (fennel.multi-sym? ast))
(reference ast scope)))
(λ define [?definition binding scope]
(λ define [?definition binding scope ?opts]
;; Add a definition to the definitions
;; recursively explore the binding (which, in the general case, is a destructuring assignment)
;; right now I'm not keeping track of *how* the symbol was destructured: just finding all the symbols for now.
@ -96,7 +96,8 @@ later by fennel-ls.language to answer requests from the client."
:referenced-by (or (?. definitions binding :referenced-by) [])
:keys (if (< 0 (length keys))
(fcollect [i 1 (length keys)]
(. keys i)))}]
(. keys i)))
:var? (?. ?opts :isvar)}]
(tset (. definitions-by-scope scope) (tostring binding) definition)
(tset definitions binding definition))
(list? binding)
@ -104,7 +105,7 @@ later by fennel-ls.language to answer requests from the client."
(= (length binding)
(- (length ?definition) 1)))
(for [i 1 (length binding)]
(define (. ?definition (+ i 1)) (. binding i) scope))
(define (. ?definition (+ i 1)) (. binding i) scope ?opts))
(recurse (. binding 1) keys))
(table? binding)
(accumulate [prev nil
@ -135,10 +136,10 @@ later by fennel-ls.language to answer requests from the client."
;; (fcollect [i 1 (length keys)]
;; (. keys i)))}
name (string.match (tostring binding) "[^%.:]+")]
(when (multisym? binding)
(case (find-definition (tostring name) scope)
target
(table.insert target.referenced-by binding))))
(case (find-definition (tostring name) scope)
target (if (multisym? binding)
(table.insert target.referenced-by binding)
(set target.var-set true))))
(= :table (type binding))
(each [k v (iter binding)]
(table.insert keys k)
@ -146,11 +147,11 @@ later by fennel-ls.language to answer requests from the client."
(table.remove keys))))
(recurse binding []))
(λ destructure [to from scope {:declaration ?declaration?}]
(λ destructure [to from scope {:declaration ?declaration? &as opts}]
;; I really don't understand symtype
;; I think I need an explanation
(if ?declaration?
(define to from scope)
(define to from scope opts)
(mutate to from scope)))
(λ add-field [ast multisym scope]

View File

@ -73,6 +73,15 @@ Goes through a file and mutates the `file.diagnostics` field, filling it with di
:code 304
:codeDescription "bad-unpack"}))))
(λ var-never-set [self file]
(icollect [symbol definition (pairs file.definitions) &into file.diagnostics]
(if (and definition.var? (not definition.var-set))
{:range (message.ast->range self file symbol)
:message (.. "var is never set: " (tostring symbol))
:severity message.severity.WARN
:code 301
:codeDescription "var-never-set"})))
(λ check [self file]
"fill up the file.diagnostics table with linting things"
(if self.configuration.checks.unused-definition
@ -82,6 +91,8 @@ Goes through a file and mutates the `file.diagnostics` field, filling it with di
(if self.configuration.checks.unnecessary-method
(unnecessary-method self file))
(if self.configuration.checks.bad-unpack
(bad-unpack self file)))
(bad-unpack self file))
(if self.configuration.checks.var-never-set
(var-never-set self file)))
{: check}

View File

@ -96,7 +96,8 @@ in the \"self\" object."
:checks {:unused-definition (option true)
:unknown-module-field (option true)
:unnecessary-method (option true)
:bad-unpack (option true)}
:bad-unpack (option true)
:var-never-set (option true)}
:extra-globals (option "")})
(λ make-configuration [?c]

View File

@ -81,6 +81,20 @@
"not found")
_ (error "did not match"))))
(it "warns about vars that are never set"
(let [self (create-client)
responses (self:open-file! filename "(var x nil) (print x)")]
(match responses
[{:params {: diagnostics}}]
(is (find [_ v (ipairs diagnostics)]
(match v
{:message "var is never set: x"
:range {:start {:character 5 :line 0}
:end {:character 6 :line 0}}}
v))
"not found")
_ (error "did not match"))))
(it "warns about unused functions"
(let [self (create-client)
responses (self:open-file! filename "(fn x [])")]