introduce mismatched-argument-count lint
This commit is contained in:
parent
772d59fff1
commit
5143310b90
@ -169,7 +169,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
|||||||
(sym? head :λ)
|
(sym? head :λ)
|
||||||
(sym? head :hashfn))))
|
(sym? head :hashfn))))
|
||||||
(search-multival server file (. definition (length definition)) stack multival opts)
|
(search-multival server file (. definition (length definition)) stack multival opts)
|
||||||
result result
|
result_ {:indeterminate true}
|
||||||
_ (case (docs.get-builtin server (tostring (. call 1)))
|
_ (case (docs.get-builtin server (tostring (. call 1)))
|
||||||
{:metadata metadata_} {:indeterminate true})))))))
|
{:metadata metadata_} {:indeterminate true})))))))
|
||||||
|
|
||||||
@ -281,19 +281,19 @@ initialization-opts: {:stack ?list[ast]
|
|||||||
_ child (ipairs ast)
|
_ child (ipairs ast)
|
||||||
&until result]
|
&until result]
|
||||||
(if (contains? child byte)
|
(if (contains? child byte)
|
||||||
(recurse child byte)))
|
(recurse child)))
|
||||||
(and (not (sym? ast)) (not (varg? ast)))
|
(and (not (sym? ast)) (not (varg? ast)))
|
||||||
(accumulate [(result _parent) nil
|
(accumulate [(result _parent) nil
|
||||||
key value (pairs ast)
|
key value (pairs ast)
|
||||||
&until result]
|
&until result]
|
||||||
(if (contains? key byte)
|
(if (contains? key byte)
|
||||||
(recurse key byte)
|
(recurse key)
|
||||||
(contains? value byte)
|
(contains? value byte)
|
||||||
(recurse value byte)))))))
|
(recurse value)))))))
|
||||||
(values
|
(values
|
||||||
(accumulate [result nil _ top-level-form (ipairs ast) &until result]
|
(accumulate [result nil _ top-level-form (ipairs ast) &until result]
|
||||||
(if (contains? top-level-form byte)
|
(if (contains? top-level-form byte)
|
||||||
(recurse top-level-form byte)))
|
(recurse top-level-form)))
|
||||||
(fcollect [i 1 (length parents)]
|
(fcollect [i 1 (length parents)]
|
||||||
(. parents (- (length parents) i -1)))))
|
(. parents (- (length parents) i -1)))))
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ in the \"server\" object."
|
|||||||
(or (get-by-uri server uri)
|
(or (get-by-uri server uri)
|
||||||
;; if the cached uri isn't found, clear the cache and try again
|
;; if the cached uri isn't found, clear the cache and try again
|
||||||
(do (tset modules module nil)
|
(do (tset modules module nil)
|
||||||
(get-by-module server module)))
|
(get-by-module server module macro?)))
|
||||||
nil
|
nil
|
||||||
(case (searcher.lookup server module macro?)
|
(case (searcher.lookup server module macro?)
|
||||||
uri
|
uri
|
||||||
|
|||||||
@ -23,7 +23,7 @@ Luckily, I'm testing with Neovim, so I can pretend these problems don't exist fo
|
|||||||
"" header ;; base case. empty line marks end of header
|
"" header ;; base case. empty line marks end of header
|
||||||
line (let [(k v) (line:match "^(.-): (.-)$")]
|
line (let [(k v) (line:match "^(.-): (.-)$")]
|
||||||
(if (not (and k v))
|
(if (not (and k v))
|
||||||
(error "fennel-ls encountered a malformed json-rpc header: \"" line "\""))
|
(error (.. "fennel-ls encountered a malformed json-rpc header: \"" line "\"")))
|
||||||
(tset header k v)
|
(tset header k v)
|
||||||
(read-header in header))))))
|
(read-header in header))))))
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(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))
|
||||||
|
(local navigate (require :fennel-ls.navigate))
|
||||||
(local dkjson (require :dkjson))
|
(local dkjson (require :dkjson))
|
||||||
|
|
||||||
(local lints {:definition []
|
(local lints {:definition []
|
||||||
@ -125,7 +126,7 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(. redundant-wrappers (tostring (. ast 1)))
|
(. redundant-wrappers (tostring (. ast 1)))
|
||||||
(= (length ast) 2))
|
(= (length ast) 2))
|
||||||
{:range (message.ast->range server file ast)
|
{:range (message.ast->range server file ast)
|
||||||
:message (.. "unnecessary " (tostring (. ast 1)))
|
:message (.. "unnecessary unary " (tostring (. ast 1)))
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:fix #{:title "Unwrap the expression"
|
:fix #{:title "Unwrap the expression"
|
||||||
:changes [{:range (message.ast->range server file ast)
|
:changes [{:range (message.ast->range server file ast)
|
||||||
@ -247,14 +248,14 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
|
|
||||||
(add-lint :inline-unpack
|
(add-lint :inline-unpack
|
||||||
{:type [:function-call :special-call]
|
{:type [:function-call :special-call]
|
||||||
:impl (fn [server file call]
|
:impl (fn [server file ast]
|
||||||
"generally, values and unpack are signs that the user is trying to do
|
"generally, values and unpack are signs that the user is trying to do
|
||||||
something with multiple values. However, multiple values will get
|
something with multiple values. However, multiple values will get
|
||||||
\"adjusted\" to one value if they don't come at the end of the call."
|
\"adjusted\" to one value if they don't come at the end of the call."
|
||||||
(faccumulate [f nil index 2 (length call) &until f]
|
(faccumulate [f nil index 2 (length ast) &until f]
|
||||||
(let [arg (. call index)]
|
(let [arg (. ast index)]
|
||||||
(if (and (or (op? (. call 1)) (not (special? (. call 1))))
|
(if (and (or (op? (. ast 1)) (not (special? (. ast 1))))
|
||||||
(not= index (length call))
|
(not= index (length ast))
|
||||||
(list? arg)
|
(list? arg)
|
||||||
(or (sym? (. arg 1) :values)
|
(or (sym? (. arg 1) :values)
|
||||||
(sym? (. arg 1) :unpack)
|
(sym? (. arg 1) :unpack)
|
||||||
@ -267,8 +268,8 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
|
|
||||||
(add-lint :empty-let
|
(add-lint :empty-let
|
||||||
{:type :special-call
|
{:type :special-call
|
||||||
:impl (fn [server file call]
|
:impl (fn [server file ast]
|
||||||
(case call
|
(case ast
|
||||||
(where [let* binding]
|
(where [let* binding]
|
||||||
(sym? let* :let)
|
(sym? let* :let)
|
||||||
(fennel.sequence? binding)
|
(fennel.sequence? binding)
|
||||||
@ -282,6 +283,42 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
{:range {: start : end}
|
{:range {: start : end}
|
||||||
:newText "do"})]}}))})
|
:newText "do"})]}}))})
|
||||||
|
|
||||||
|
(fn possibly-multival? [ast]
|
||||||
|
(or (fennel.list? ast)
|
||||||
|
(fennel.varg? ast)))
|
||||||
|
|
||||||
|
(add-lint :mismatched-argument-count
|
||||||
|
{:type [:function-call :special-call :macro-call]
|
||||||
|
:disabled true
|
||||||
|
:impl (fn [server file ast]
|
||||||
|
(case (analyzer.search server file (. ast 1) {} {})
|
||||||
|
{:indeterminate nil &as result}
|
||||||
|
(case (?. (navigate.getmetadata server result) :fnl/arglist)
|
||||||
|
signature
|
||||||
|
(let [number-of-args (- (length ast) 1)
|
||||||
|
passes-extra-args (and (not= 1 (length ast)) (possibly-multival? (. ast (length ast))))
|
||||||
|
(number-of-params accepts-extra-params) (faccumulate [(last-required-argument vararg) nil
|
||||||
|
i (length signature) 1 -1]
|
||||||
|
(let [s (tostring (. signature i))
|
||||||
|
m (or (= s "...") (= s "&"))]
|
||||||
|
(values
|
||||||
|
(if m
|
||||||
|
nil
|
||||||
|
last-required-argument
|
||||||
|
last-required-argument
|
||||||
|
(not= (string.sub s 1 1) "?")
|
||||||
|
i)
|
||||||
|
(or vararg m))))
|
||||||
|
number-of-params (or number-of-params 0)]
|
||||||
|
(if (and (< number-of-args number-of-params) (not passes-extra-args))
|
||||||
|
{:range (message.ast->range server file ast)
|
||||||
|
:message (.. "not enough args. my analysis of the signature says you need at least " number-of-params " arguments but I only see " number-of-args)
|
||||||
|
:severity message.severity.WARN}
|
||||||
|
(and (< (length signature) number-of-args) (not accepts-extra-params))
|
||||||
|
{:range (message.ast->range server file ast)
|
||||||
|
:message (.. "too many args. my analysis of the signature says we ignore any arguments past " number-of-params " arguments but you've provided " number-of-args)
|
||||||
|
:severity message.severity.WARN})))))})
|
||||||
|
|
||||||
(local lint-mt {:__tojson (fn [{: self} state] (dkjson.encode self state))
|
(local lint-mt {:__tojson (fn [{: self} state] (dkjson.encode self state))
|
||||||
:__index #(. $1 :self $2)})
|
:__index #(. $1 :self $2)})
|
||||||
|
|
||||||
|
|||||||
@ -201,18 +201,18 @@
|
|||||||
(assert-ok "(do (print :x) 11)")
|
(assert-ok "(do (print :x) 11)")
|
||||||
;; unnecessary do
|
;; unnecessary do
|
||||||
(check "(do 9)"
|
(check "(do 9)"
|
||||||
[{:message "unnecessary do"
|
[{:message "unnecessary unary do"
|
||||||
:code :unnecessary-unary
|
:code :unnecessary-unary
|
||||||
:range {:start {:character 0 :line 0}
|
:range {:start {:character 0 :line 0}
|
||||||
:end {:character 6 :line 0}}}])
|
:end {:character 6 :line 0}}}])
|
||||||
;; unnecessary values
|
;; unnecessary values
|
||||||
(check "(print :hey (values :lol))"
|
(check "(print :hey (values :lol))"
|
||||||
[{:code :unnecessary-unary
|
[{:code :unnecessary-unary
|
||||||
:message "unnecessary values"
|
:message "unnecessary unary values"
|
||||||
:range {:start {:character 12 :line 0}
|
:range {:start {:character 12 :line 0}
|
||||||
:end {:character 25 :line 0}}}])
|
:end {:character 25 :line 0}}}])
|
||||||
(check "(+ (* 3) (* 4 4))"
|
(check "(+ (* 3) (* 4 4))"
|
||||||
[{:message "unnecessary *"
|
[{:message "unnecessary unary *"
|
||||||
:code :unnecessary-unary
|
:code :unnecessary-unary
|
||||||
:range {:start {:character 3 :line 0}
|
:range {:start {:character 3 :line 0}
|
||||||
:end {:character 8 :line 0}}}])
|
:end {:character 8 :line 0}}}])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user