Add unnecessary-tset, unnecessary-do, and redundant-do lints.
"Redundant" means that it's a `do` in a context where there's already an implicit `do`, as defined by fennel.syntax indicating body-form? is true. Unnecessary means it only has one argument.
This commit is contained in:
parent
a27f42bfa4
commit
b5dbeb28fe
@ -23,6 +23,9 @@ There are no global settings. They're all stored in the `server` object.
|
|||||||
:lints {:unused-definition (option true)
|
:lints {:unused-definition (option true)
|
||||||
:unknown-module-field (option true)
|
:unknown-module-field (option true)
|
||||||
:unnecessary-method (option true)
|
:unnecessary-method (option true)
|
||||||
|
:unnecessary-tset (option true)
|
||||||
|
:unnecessary-do (option true)
|
||||||
|
:redundant-do (option true)
|
||||||
:match-should-case (option true)
|
:match-should-case (option true)
|
||||||
:bad-unpack (option true)
|
:bad-unpack (option true)
|
||||||
:var-never-set (option true)
|
:var-never-set (option true)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
Provides the function (check server file), which goes through a file and mutates
|
Provides the function (check server file), which goes through a file and mutates
|
||||||
the `file.diagnostics` field, filling it with diagnostics."
|
the `file.diagnostics` field, filling it with diagnostics."
|
||||||
|
|
||||||
(local {: sym? : list? : table? : view} (require :fennel))
|
(local {: sym? : list? : table? : view &as fennel} (require :fennel))
|
||||||
(local analyzer (require :fennel-ls.analyzer))
|
(local analyzer (require :fennel-ls.analyzer))
|
||||||
(local message (require :fennel-ls.message))
|
(local message (require :fennel-ls.message))
|
||||||
(local utils (require :fennel-ls.utils))
|
(local utils (require :fennel-ls.utils))
|
||||||
@ -81,6 +81,48 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
:code 303
|
:code 303
|
||||||
:codeDescription "unnecessary-method"}))))
|
:codeDescription "unnecessary-method"}))))
|
||||||
|
|
||||||
|
(λ unnecessary-tset [server file head call]
|
||||||
|
(if (and (sym? head :tset) (sym? (. call 2))
|
||||||
|
(= :string (type (. call 3))) (. file.lexical call))
|
||||||
|
(diagnostic {:range (message.ast->range server file call)
|
||||||
|
:message (string.format "unnecessary %s" head)
|
||||||
|
:severity message.severity.WARN
|
||||||
|
:code 309
|
||||||
|
:codeDescription "unnecessary-tset"}
|
||||||
|
#[{:range (message.ast->range server file call)
|
||||||
|
:newText (string.format "(set %s.%s %s)"
|
||||||
|
(. call 2) (. call 3)
|
||||||
|
(view (. call 4)))}])))
|
||||||
|
|
||||||
|
(λ unnecessary-do-values [server file head call]
|
||||||
|
(if (and (or (sym? head :do) (sym? head :values))
|
||||||
|
(= nil (. call 3)) (. file.lexical call))
|
||||||
|
(diagnostic {:range (message.ast->range server file call)
|
||||||
|
:message (string.format "unnecessary %s" head)
|
||||||
|
:severity message.severity.WARN
|
||||||
|
:code 310
|
||||||
|
:codeDescription "unnecessary-do-values"}
|
||||||
|
#[{:range (message.ast->range server file call)
|
||||||
|
:newText (view (. call 2))}])))
|
||||||
|
|
||||||
|
(local implicit-do-forms (collect [form {: body-form?} (pairs (fennel.syntax))]
|
||||||
|
(values form body-form?)))
|
||||||
|
|
||||||
|
(λ redundant-do [server file head call]
|
||||||
|
(let [last-body (. call (length call))]
|
||||||
|
(if (and (. implicit-do-forms (tostring head)) (. file.lexical call)
|
||||||
|
(list? last-body) (sym? (. last-body 1) :do))
|
||||||
|
(diagnostic {:range (message.ast->range server file last-body)
|
||||||
|
:message "redundant do"
|
||||||
|
:severity message.severity.WARN
|
||||||
|
:code 311
|
||||||
|
:codeDescription "redundant-do"}
|
||||||
|
#[{:range (message.ast->range server file last-body)
|
||||||
|
:newText (table.concat
|
||||||
|
(fcollect [i 2 (length last-body)]
|
||||||
|
(view (. last-body i)))
|
||||||
|
" ")}]))))
|
||||||
|
|
||||||
(λ bad-unpack [server file op call]
|
(λ bad-unpack [server file op call]
|
||||||
"an unpack call leading into an operator"
|
"an unpack call leading into an operator"
|
||||||
(let [last-item (. call (length call))]
|
(let [last-item (. call (length call))]
|
||||||
@ -185,6 +227,9 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(when head
|
(when head
|
||||||
(if lints.bad-unpack (table.insert diagnostics (bad-unpack server file head call)))
|
(if lints.bad-unpack (table.insert diagnostics (bad-unpack server file head call)))
|
||||||
(if lints.unnecessary-method (table.insert diagnostics (unnecessary-method server file head call)))
|
(if lints.unnecessary-method (table.insert diagnostics (unnecessary-method server file head call)))
|
||||||
|
(if lints.unnecessary-do (table.insert diagnostics (unnecessary-do-values server file head call)))
|
||||||
|
(if lints.unnecessary-tset (table.insert diagnostics (unnecessary-tset server file head call)))
|
||||||
|
(if lints.redundant-do (table.insert diagnostics (redundant-do server file head call)))
|
||||||
(if lints.op-with-no-arguments (table.insert diagnostics (op-with-no-arguments server file head call)))
|
(if lints.op-with-no-arguments (table.insert diagnostics (op-with-no-arguments server file head call)))
|
||||||
|
|
||||||
;; argument lints
|
;; argument lints
|
||||||
|
|||||||
@ -177,6 +177,47 @@
|
|||||||
[] [{:code 307}])
|
[] [{:code 307}])
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(fn test-unnecessary-tset []
|
||||||
|
;; valid, if you're targeting older Fennels
|
||||||
|
(check "(local [tbl key] [{} :k]) (tset tbl key 249)" [] [{}])
|
||||||
|
;; never a good use of tset
|
||||||
|
(check "(local tbl {}) (tset tbl :key 9)"
|
||||||
|
[{:code 309
|
||||||
|
:codeDescription "unnecessary-tset"
|
||||||
|
:message "unnecessary tset"
|
||||||
|
:range {:start {:character 15 :line 0}
|
||||||
|
:end {:character 32 :line 0}}}] []))
|
||||||
|
|
||||||
|
(fn test-unnecessary-do []
|
||||||
|
;; multi-arg do
|
||||||
|
(check "(do (print :x) 11)" [] [{}])
|
||||||
|
;; unnecessary do
|
||||||
|
(check "(do 9)" [{:message "unnecessary do"
|
||||||
|
:code 310
|
||||||
|
:codeDescription "unnecessary-do-values"
|
||||||
|
:range {:start {:character 0 :line 0}
|
||||||
|
:end {:character 6 :line 0}}}] [])
|
||||||
|
;; unnecessary values
|
||||||
|
(check "(print :hey (values :lol))"
|
||||||
|
[{:code 310
|
||||||
|
:codeDescription "unnecessary-do-values"
|
||||||
|
:message "unnecessary values"
|
||||||
|
:range {:start {:character 12 :line 0}
|
||||||
|
:end {:character 25 :line 0}}}]
|
||||||
|
[]))
|
||||||
|
|
||||||
|
(fn test-redundant-do []
|
||||||
|
;; good do
|
||||||
|
(check "(case 134 x (do (print :x x) 11))" [] [{}])
|
||||||
|
;; unnecessary one
|
||||||
|
(set _G.dbg true)
|
||||||
|
(check "(let [x 29] (do (print 9) x))"
|
||||||
|
[{:code 311
|
||||||
|
:codeDescription "redundant-do"
|
||||||
|
:message "redundant do"
|
||||||
|
:range {:start {:character 12 :line 0}
|
||||||
|
:end {:character 28 :line 0}}}] []))
|
||||||
|
|
||||||
(fn test-match-should-case []
|
(fn test-match-should-case []
|
||||||
;; OK: most basic pinning
|
;; OK: most basic pinning
|
||||||
(check "(let [x 99] (match 99 x :yep!))" [] [{}])
|
(check "(let [x 99] (match 99 x :yep!))" [] [{}])
|
||||||
@ -207,16 +248,12 @@
|
|||||||
:end {:character 6 :line 0}}}] []))
|
:end {:character 6 :line 0}}}] []))
|
||||||
|
|
||||||
;; TODO lints:
|
;; TODO lints:
|
||||||
;; unnecessary (do) in body position
|
|
||||||
;; duplicate keys in kv table
|
;; duplicate keys in kv table
|
||||||
;; (tset <sym> <str>) --> (set <sym>.<str>)
|
;; (tset <sym> <any>) --> (set (. <sym> <any>)) (might be wanted for compat?)
|
||||||
;; (tset <sym> <any>) --> (set (. <sym> <any>))
|
|
||||||
;; {&as x} and [&as x] pattern with no other matches
|
;; {&as x} and [&as x] pattern with no other matches
|
||||||
;; Unused variables / fields (maybe difficult)
|
;; Unused variables / fields (maybe difficult)
|
||||||
;; discarding results to various calls, such as unpack, values, etc
|
;; discarding results to various calls, such as unpack, values, etc
|
||||||
;; unnecessary `do`/`values` with only one inner form
|
|
||||||
;; `pairs` or `ipairs` call in a (for) binding table
|
;; `pairs` or `ipairs` call in a (for) binding table
|
||||||
;; mark when unification is happening on a `match` pattern (may be difficult)
|
|
||||||
;; steal as many lints as possible from cargo
|
;; steal as many lints as possible from cargo
|
||||||
;; unnecessary parens around single multival destructure
|
;; unnecessary parens around single multival destructure
|
||||||
|
|
||||||
@ -226,6 +263,9 @@
|
|||||||
: test-ampersand
|
: test-ampersand
|
||||||
: test-unknown-module-field
|
: test-unknown-module-field
|
||||||
: test-unnecessary-colon
|
: test-unnecessary-colon
|
||||||
|
: test-unnecessary-tset
|
||||||
|
: test-unnecessary-do
|
||||||
|
: test-redundant-do
|
||||||
: test-unset-var
|
: test-unset-var
|
||||||
: test-match-should-case
|
: test-match-should-case
|
||||||
: test-unpack-into-op
|
: test-unpack-into-op
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user