New nested-associative-operator lint
I want to also get like (if a (if b c)) at some point, but this lint only covers the ones where flattening fixes it always.
This commit is contained in:
parent
ea826576e3
commit
0817af9488
@ -10,6 +10,7 @@
|
||||
* Disabled by default because it requires following fennel's optional argument naming conventions
|
||||
* New lint `invalid-flsproject-settings` checks your `flsproject.fnl` file as you edit it
|
||||
* Renamed lint `unnecessary-do-values` to `unnecessary-unary` and made it apply to many more forms
|
||||
* New lint `nested-associative-operator` checks for nested operations that could be flattened (eg. `(+ 1 2 (+ 3 4) 5)`)
|
||||
|
||||
### Changes
|
||||
* Updated to dkjson 2.8
|
||||
|
||||
@ -782,6 +782,54 @@ You can read more about how to add lints in docs/linting.md"
|
||||
:severity message.severity.WARN}))))
|
||||
nil)})
|
||||
|
||||
(local associative-ops {:and true :or true :+ true :* true :band true :bor true :.. true})
|
||||
|
||||
(add-lint :nested-associative-operator
|
||||
{:what-it-does
|
||||
"Identifies forms that could be written in a flattr way, like `(and foo (and bar baz))`."
|
||||
:why-care?
|
||||
"Collapsing nested forms reduces unnecessary nesting and makes code more readable and idiomatic."
|
||||
:example
|
||||
"```fnl
|
||||
(and foo bar (and baz buzz) xyz)
|
||||
(+ a (+ b c) d)
|
||||
(or x (or y z))
|
||||
```
|
||||
|
||||
Instead, use:
|
||||
```fnl
|
||||
;; Flattened forms:
|
||||
(and foo bar baz buzz xyz)
|
||||
(+ a b c d)
|
||||
(or x y z)
|
||||
```"
|
||||
:since "0.2.2-dev"
|
||||
:type :special-call
|
||||
:impl (fn [server file ast]
|
||||
(let [op (. ast 1)]
|
||||
(when (and (sym? op)
|
||||
(. associative-ops (tostring op)))
|
||||
(faccumulate [diagnostic nil
|
||||
i 2 (length ast)
|
||||
&until diagnostic]
|
||||
(let [arg (. ast i)
|
||||
op-str (tostring op)]
|
||||
(when (and (list? arg) (= op (. arg 1)))
|
||||
{:range (message.ast->range server file arg)
|
||||
:message (.. "nested " op-str " can be collapsed")
|
||||
:severity message.severity.WARN
|
||||
:fix #(let [new-form (list (. ast 1))]
|
||||
(for [j 2 (length ast)]
|
||||
(let [item (. ast j)]
|
||||
(if (and (list? item)
|
||||
(sym? (. item 1) op-str))
|
||||
(fcollect [k 2 (length item) &into new-form]
|
||||
(. item k))
|
||||
(table.insert new-form item))))
|
||||
{:title (.. "Collapse all nested " op-str)
|
||||
:changes [{:range (message.ast->range server file ast)
|
||||
:newText (view new-form)}]})}))))))})
|
||||
|
||||
(local lint-mt {:__tojson (fn [{: self} state] (dkjson.encode self state))
|
||||
:__index #(. $1 :self $2)})
|
||||
|
||||
|
||||
@ -347,6 +347,29 @@
|
||||
|
||||
;; unused variable, when a function binding is only used in its body, and the function value is discarded
|
||||
|
||||
(fn test-nested-associative-operator []
|
||||
(check "(and foo (and bar baz) xyz)"
|
||||
[{:message "nested and can be collapsed"
|
||||
:code :nested-associative-operator}])
|
||||
|
||||
(check "(+ a (+ b c) d)"
|
||||
[{:message "nested + can be collapsed"
|
||||
:code :nested-associative-operator}])
|
||||
|
||||
(check "(or x (or y z))"
|
||||
[{:message "nested or can be collapsed"
|
||||
:code :nested-associative-operator}])
|
||||
|
||||
(check "(and foo (and bar baz) (and this that))"
|
||||
[{:message "nested and can be collapsed"
|
||||
:code :nested-associative-operator}])
|
||||
|
||||
(assert-ok "(and true false true)") ; no nesting
|
||||
(assert-ok "(+ 1 2 3)") ; no nesting
|
||||
(assert-ok "(* (+ 1 2) 3)") ; different operations
|
||||
(assert-ok "(and true (or false true))") ; different operators
|
||||
nil)
|
||||
|
||||
{: test-unused
|
||||
: test-ampersand
|
||||
: test-unknown-module-field
|
||||
@ -362,4 +385,5 @@
|
||||
: test-empty-let
|
||||
: test-decreasing-comparison
|
||||
: test-arg-count
|
||||
: test-duplicate-keys}
|
||||
: test-duplicate-keys
|
||||
: test-nested-associative-operator}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user