From a521701dbabf8b06c9d442f8a12df02bee8e3bf4 Mon Sep 17 00:00:00 2001 From: XeroOl Date: Fri, 26 May 2023 18:17:15 -0500 Subject: [PATCH] Fix "unused variable" lint not counting mutating sets as a use --- src/fennel-ls/compiler.fnl | 27 ++++++++++++++++++++++++++- test/diagnostic-test.fnl | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index 740b340..a624f3c 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -67,6 +67,30 @@ later by fennel-ls.language to answer requests from the client." (if ?reference? (reference ast scope))) + (λ mutate [?definition binding scope] + ;; for now, mutating a field counts as a reference I guess + (λ recurse [binding keys] + (if (sym? binding) + (let [ + ;; ;; future work may need to care about mutations + ;; _mutation + ;; {: binding + ;; :new-definition ?definition + ;; :keys (if (< 0 (length keys)) + ;; (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)))) + (= :table (type binding)) + (each [k v (iter binding)] + (table.insert keys k) + (recurse v keys) + (table.remove keys)))) + (recurse binding [])) + (λ define [?definition binding scope] ;; Add a definition to the definitions ;; recursively explore the binding (which, in the general case, is a destructuring assignment) @@ -94,7 +118,8 @@ later by fennel-ls.language to answer requests from the client." ;; I really don't understand symtype ;; I think I need an explanation (if ?declaration? - (define to from scope))) + (define to from scope) + (mutate to from scope))) (λ define-function-name [ast scope] ;; add a function definition to the definitions diff --git a/test/diagnostic-test.fnl b/test/diagnostic-test.fnl index 78de013..66faa19 100644 --- a/test/diagnostic-test.fnl +++ b/test/diagnostic-test.fnl @@ -82,8 +82,30 @@ :end {:character 8 :line 0}}} v)) "not found") + _ (error "did not match")))) + + (it "does not warn if a field is used" + (let [self (create-client) + responses (self:open-file! filename "(fn [abc] (set abc.xyz 10))")] + (assert (not (?. responses 1 :params :diagnostics 1))))) + + (it "warns if a var is written but not read" + (let [self (create-client) + responses (self:open-file! filename "(var x 1) (set x 2) (set [x] [3])")] + (match responses + [{:params {: diagnostics}}] + (is (find [i v (ipairs diagnostics)] + (match v + {:message "unused definition: x" + :range {:start {:character 5 :line 0} + :end {:character 6 :line 0}}} + v)) + "not found") _ (error "did not match"))))) + + + ;; TODO lints: ;; unnecessary (do) in body position ;; Unused variables / fields (maybe difficult)