introduce mismatched-argument-count lint

This commit is contained in:
XeroOl 2025-07-11 19:16:06 -05:00
parent 772d59fff1
commit 5143310b90
5 changed files with 55 additions and 18 deletions

View File

@ -169,7 +169,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
(sym? head :λ)
(sym? head :hashfn))))
(search-multival server file (. definition (length definition)) stack multival opts)
result result
result_ {:indeterminate true}
_ (case (docs.get-builtin server (tostring (. call 1)))
{:metadata metadata_} {:indeterminate true})))))))
@ -281,19 +281,19 @@ initialization-opts: {:stack ?list[ast]
_ child (ipairs ast)
&until result]
(if (contains? child byte)
(recurse child byte)))
(recurse child)))
(and (not (sym? ast)) (not (varg? ast)))
(accumulate [(result _parent) nil
key value (pairs ast)
&until result]
(if (contains? key byte)
(recurse key byte)
(recurse key)
(contains? value byte)
(recurse value byte)))))))
(recurse value)))))))
(values
(accumulate [result nil _ top-level-form (ipairs ast) &until result]
(if (contains? top-level-form byte)
(recurse top-level-form byte)))
(recurse top-level-form)))
(fcollect [i 1 (length parents)]
(. parents (- (length parents) i -1)))))

View File

@ -38,7 +38,7 @@ in the \"server\" object."
(or (get-by-uri server uri)
;; if the cached uri isn't found, clear the cache and try again
(do (tset modules module nil)
(get-by-module server module)))
(get-by-module server module macro?)))
nil
(case (searcher.lookup server module macro?)
uri

View File

@ -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
line (let [(k v) (line:match "^(.-): (.-)$")]
(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)
(read-header in header))))))

View File

@ -8,6 +8,7 @@ the `file.diagnostics` field, filling it with diagnostics."
(local analyzer (require :fennel-ls.analyzer))
(local message (require :fennel-ls.message))
(local utils (require :fennel-ls.utils))
(local navigate (require :fennel-ls.navigate))
(local dkjson (require :dkjson))
(local lints {:definition []
@ -125,7 +126,7 @@ the `file.diagnostics` field, filling it with diagnostics."
(. redundant-wrappers (tostring (. ast 1)))
(= (length ast) 2))
{:range (message.ast->range server file ast)
:message (.. "unnecessary " (tostring (. ast 1)))
:message (.. "unnecessary unary " (tostring (. ast 1)))
:severity message.severity.WARN
:fix #{:title "Unwrap the expression"
:changes [{:range (message.ast->range server file ast)
@ -247,14 +248,14 @@ the `file.diagnostics` field, filling it with diagnostics."
(add-lint :inline-unpack
{: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
something with multiple values. However, multiple values will get
\"adjusted\" to one value if they don't come at the end of the call."
(faccumulate [f nil index 2 (length call) &until f]
(let [arg (. call index)]
(if (and (or (op? (. call 1)) (not (special? (. call 1))))
(not= index (length call))
(faccumulate [f nil index 2 (length ast) &until f]
(let [arg (. ast index)]
(if (and (or (op? (. ast 1)) (not (special? (. ast 1))))
(not= index (length ast))
(list? arg)
(or (sym? (. arg 1) :values)
(sym? (. arg 1) :unpack)
@ -267,8 +268,8 @@ the `file.diagnostics` field, filling it with diagnostics."
(add-lint :empty-let
{:type :special-call
:impl (fn [server file call]
(case call
:impl (fn [server file ast]
(case ast
(where [let* binding]
(sym? let* :let)
(fennel.sequence? binding)
@ -282,6 +283,42 @@ the `file.diagnostics` field, filling it with diagnostics."
{:range {: start : end}
: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))
:__index #(. $1 :self $2)})

View File

@ -201,18 +201,18 @@
(assert-ok "(do (print :x) 11)")
;; unnecessary do
(check "(do 9)"
[{:message "unnecessary do"
[{:message "unnecessary unary do"
:code :unnecessary-unary
:range {:start {:character 0 :line 0}
:end {:character 6 :line 0}}}])
;; unnecessary values
(check "(print :hey (values :lol))"
[{:code :unnecessary-unary
:message "unnecessary values"
:message "unnecessary unary values"
:range {:start {:character 12 :line 0}
:end {:character 25 :line 0}}}])
(check "(+ (* 3) (* 4 4))"
[{:message "unnecessary *"
[{:message "unnecessary unary *"
:code :unnecessary-unary
:range {:start {:character 3 :line 0}
:end {:character 8 :line 0}}}])