From b861812b244aa27af31a2775e3f146d5878413f8 Mon Sep 17 00:00:00 2001 From: XeroOl Date: Sun, 26 Nov 2023 12:38:20 -0600 Subject: [PATCH] Re-enable the : lint, but with better checks --- src/fennel-ls/compiler.fnl | 18 +++++++++++++++--- src/fennel-ls/diagnostics.fnl | 16 ++++++++++------ src/fennel-ls/state.fnl | 2 +- test/diagnostic-test.fnl | 28 ++++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index aa2c71e..ecaec76 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -59,8 +59,9 @@ later by fennel-ls.language to answer requests from the client." diagnostics {} ; [diagnostic] references {} ; symbol -> references scopes {} ; ast -> scope - require-calls {} ; ast -> boolean (does this ast start with the symbol `require) - calls {}]; array of all lists + calls {} ; all calls in the macro-expanded code -> true + lexical {} ; all lists and tables in the original source + require-calls {}] ; the keys are all the calls that start with `require (λ find-definition [name ?scope] (when ?scope @@ -206,7 +207,7 @@ later by fennel-ls.language to answer requests from the client." (tset scopes ast scope)) (λ call [ast scope] - (tset calls ast (. ast 1)) + (tset calls ast true) (tset scopes ast scope) ;; Most calls aren't interesting, but here's the list of the ones that are: (case ast @@ -308,6 +309,16 @@ later by fennel-ls.language to answer requests from the client." ast (icollect [ok ast parser &until (not ok)] ast)] + (λ collect-everything [ast result] + (when (or (table? ast) (list? ast)) + (tset result ast true) + (each [k v (iter ast)] + (collect-everything k result) + (collect-everything v result)))) + + (collect-everything ast lexical) + + ;; This is bad; we mutate fennel.macro-path (let [old-macro-path fennel.macro-path] @@ -322,6 +333,7 @@ later by fennel-ls.language to answer requests from the client." (set file.ast ast) (set file.calls calls) + (set file.lexical lexical) (set file.scope scope) (set file.scopes scopes) (set file.definitions definitions) diff --git a/src/fennel-ls/diagnostics.fnl b/src/fennel-ls/diagnostics.fnl index 2e75857..64557a7 100644 --- a/src/fennel-ls/diagnostics.fnl +++ b/src/fennel-ls/diagnostics.fnl @@ -16,7 +16,7 @@ Goes through a file and mutates the `file.diagnostics` field, filling it with di :message (.. "unused definition: " (tostring symbol)) :severity message.severity.WARN :code 301 - :codeDescription "warning error"}))) + :codeDescription "I don't know"}))) (λ unknown-module-field [self file] "any multisym whose definition can't be found through a (require) call" @@ -26,7 +26,7 @@ Goes through a file and mutates the `file.diagnostics` field, filling it with di item (language.search self file symbol [] opts)] (if (and (not item) opts.searched-through-require) {:range (message.ast->range self file symbol) - :message (.. "unknown field " (tostring symbol)) + :message (.. "unknown field: " (tostring symbol)) :severity message.severity.WARN :code 302 :codeDescription "field checking I guess"}))))) @@ -34,14 +34,18 @@ Goes through a file and mutates the `file.diagnostics` field, filling it with di (λ unnecessary-method [self file] (icollect [[colon receiver method &as call] (pairs file.calls) &into file.diagnostics] - (if (and (= (fennel.sym ":") colon) + (if (and (fennel.sym? colon ":") (fennel.sym? receiver) - (= :string (type method))) + (. file.lexical call) + (= :string (type method)) + (not (method:find "^[0-9]")) + ;; questions: # + (not (method:find "[^!$%*+-/0-9<=>?A-Z\\^_a-z|\128-\255]"))) (case (message.ast->range self file call) range {: range - :message "unnecessary : call; use multisym" + :message (.. "unnecessary : call: use (" (tostring receiver) ":" method ")") :severity message.severity.WARN - :code 302 + :code 303 :codeDescription "unnecessary colon"})))) (λ check [self file] diff --git a/src/fennel-ls/state.fnl b/src/fennel-ls/state.fnl index 2f0abb8..c6dcdd8 100644 --- a/src/fennel-ls/state.fnl +++ b/src/fennel-ls/state.fnl @@ -96,7 +96,7 @@ in the \"self\" object." :version (option "lua54") :checks {:unused-definition (option true) :unknown-module-field (option true) - :unnecessary-method (option false)}}) + :unnecessary-method (option true)}}) (λ make-configuration [?c] (make-configuration-from-template default-configuration ?c)) diff --git a/test/diagnostic-test.fnl b/test/diagnostic-test.fnl index 1f75a54..935f053 100644 --- a/test/diagnostic-test.fnl +++ b/test/diagnostic-test.fnl @@ -104,14 +104,38 @@ (let [self (create-client)] (match (self:open-file! filename "(let [x :haha] (: x :find :a))") [{:params {: diagnostics}}] - (is (find [i v (ipairs diagnostics)] + (is (find [_ v (ipairs diagnostics)] (match v - {:message "unnecessary : call; use multisym" + {:message "unnecessary : call: use (x:find)" + :code 303 :range {:start {:character 15 :line 0} :end {:character 29 :line 0}}} v))) _ (error "did not match")))) + (it "doesn't warn when using the : special when macros are involved" + (let [self (create-client)] + (match (self:open-file! filename "(let [x :haha y :find] (-> x (: y :a)) + (let [x :haha] (-> x (: :find :a))") + [{:params {: diagnostics}}] + (is.nil (find [_ v (ipairs diagnostics)] + (match v + {:code 303 + :range _} + v))) + _ (error "did not match")))) + + (it "doesn't warn when using the : special when the string isn't valid" + (let [self (create-client)] + (match (self:open-file! filename "(let [x :haha] (: x \"bar baz\"))") + [{:params {: diagnostics}}] + (is.nil (find [_ v (ipairs diagnostics)] + (match v + {:code 303 + :range _} + v))) + _ (error "did not match")))) + (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])")]