stop tracking doc/lints.md
This commit is contained in:
parent
ff601e4c17
commit
b0760a2aad
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,5 +3,6 @@
|
|||||||
/lua*.html
|
/lua*.html
|
||||||
/build
|
/build
|
||||||
/src/fennel-ls/docs/generated/love2d.fnl
|
/src/fennel-ls/docs/generated/love2d.fnl
|
||||||
|
/doc/lints.md
|
||||||
/scratch.fnl
|
/scratch.fnl
|
||||||
/scratch-macros.fnl
|
/scratch-macros.fnl
|
||||||
|
|||||||
393
docs/lints.md
393
docs/lints.md
@ -1,393 +0,0 @@
|
|||||||
# unused-definition
|
|
||||||
## What it does
|
|
||||||
Marks bindings that aren't read. Completely overwriting a value doesn't count
|
|
||||||
as reading it. A variable that starts or ends with an `_` will not trigger this
|
|
||||||
lint. Use this to suppress the lint.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Unused definitions can lead to bugs and make code harder to understand. Either
|
|
||||||
remove the binding, or add an `_` to the variable name.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(var value 100)
|
|
||||||
(set value 10)
|
|
||||||
```
|
|
||||||
Instead, use the value, remove it, or add `_` to the variable name.
|
|
||||||
```fnl
|
|
||||||
(var value 100)
|
|
||||||
(set value 10)
|
|
||||||
;; use the value
|
|
||||||
(print value)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
Fennel's pattern matching macros also check for leading `_` in symbols.
|
|
||||||
This means that adding `_` can change the semantics of the code. In this
|
|
||||||
situation, the user needs to add the `_` to the **end** of the symbol
|
|
||||||
to disable only the lint, without changing the pattern's meaning.
|
|
||||||
Only use a trailing underscore when it's required to prevent code from
|
|
||||||
changing meaning.
|
|
||||||
```fnl
|
|
||||||
;; Original. Works, but `b` is flagged by the lint
|
|
||||||
(match [10 nil]
|
|
||||||
[a b] (print a "unintended")
|
|
||||||
_ (print "we want this one")) ;; Prints this one!
|
|
||||||
|
|
||||||
;; Suppressing lint normally causes problems
|
|
||||||
(match [10 nil]
|
|
||||||
[a _b] (print a "unintended") ;; Uh oh, we're printing "unintended" now!
|
|
||||||
_ (print "we want this one"))
|
|
||||||
|
|
||||||
;; Solution! Underscore at the end
|
|
||||||
(match [10 nil]
|
|
||||||
[a b_] (print a "unintended")
|
|
||||||
_ (print "we want this one")) ;; Prints the right one
|
|
||||||
```
|
|
||||||
|
|
||||||
Think of the trailing underscore as the fourth possible sigil:
|
|
||||||
`?identifier` - must be used, and can be `nil`
|
|
||||||
`identifier` - must be used, and should be non-`nil`
|
|
||||||
`_identifier` - may be unused, and can be `nil`
|
|
||||||
`identifer_` - may be unused, but should be non-`nil`
|
|
||||||
|
|
||||||
# unknown-module-field
|
|
||||||
## What it does
|
|
||||||
Looks for module fields that can't be statically determined to exist. This only
|
|
||||||
triggers if the module is found, but there's no definition of the field inside
|
|
||||||
of the module.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
This is probably a typo, or a missing function in the module.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
;;; in `a.fnl`
|
|
||||||
{: print}
|
|
||||||
|
|
||||||
;;; in `b.fnl`
|
|
||||||
(local a (require :a))
|
|
||||||
(a.printtt 100)
|
|
||||||
```
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
;;; in `b.fnl`
|
|
||||||
(local a (require :a))
|
|
||||||
(a.print 100) ; typo fixed
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
Fennel-ls doesn't have a full type system, so we're not able to check every
|
|
||||||
multisym statically, but as a heuristic, usually modules are able to be
|
|
||||||
evaluated statically. If you have a module that can't be figured out, please
|
|
||||||
let us know on the bug tracker.
|
|
||||||
|
|
||||||
# unnecessary-method
|
|
||||||
## What it does
|
|
||||||
Checks for unnecessary uses of the `:` method call syntax when a simple multisym
|
|
||||||
would work.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Using the method call syntax unnecessarily adds complexity and can make code
|
|
||||||
harder to understand.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(: alien :shoot-laser {:x 10 :y 20})
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(alien:shoot-laser {:x 10 :y 20})
|
|
||||||
```
|
|
||||||
|
|
||||||
# unnecessary-tset
|
|
||||||
## What it does
|
|
||||||
Identifies unnecessary uses of `tset` when a `set` with a multisym would be clearer.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Using `tset` makes the code more verbose and harder to read when a simpler
|
|
||||||
alternative exists.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(tset alien :health 1337)
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(set alien.health 1337)
|
|
||||||
```
|
|
||||||
|
|
||||||
# unnecessary-unary
|
|
||||||
## What it does
|
|
||||||
Warns about unnecessary `do` or `values` forms that only contain a single expression.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Extra forms that don't do anything add syntactic noise.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(do (print "hello"))
|
|
||||||
|
|
||||||
(values (+ 1 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(print "hello")
|
|
||||||
|
|
||||||
(+ 1 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
# redundant-do
|
|
||||||
## What it does
|
|
||||||
Identifies redundant `do` blocks within implicit do forms like `fn`, `let`, etc.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Redundant `do` blocks add unnecessary nesting and make code harder to read.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(fn [] (do
|
|
||||||
(print "first")
|
|
||||||
(print "second")))
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(fn []
|
|
||||||
(print "first")
|
|
||||||
(print "second"))
|
|
||||||
```
|
|
||||||
|
|
||||||
# bad-unpack
|
|
||||||
## What it does
|
|
||||||
Warns when `unpack` or `table.unpack` is used with operators that aren't
|
|
||||||
variadic at runtime.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Fennel operators like `+`, `*`, etc. look like they should work with `unpack`,
|
|
||||||
but they don't actually accept a variable number of arguments at runtime.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(+ 1 (unpack [2 3 4])) ; Only adds 1 and 2
|
|
||||||
(.. (unpack ["a" "b" "c"])) ; Only concatenates "a"
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
;; For concatenation:
|
|
||||||
(table.concat ["a" "b" "c"])
|
|
||||||
|
|
||||||
;; For other operators, use a loop:
|
|
||||||
(accumulate [sum 0 _ n (ipairs [1 2 3 4])]
|
|
||||||
(+ sum n))
|
|
||||||
```
|
|
||||||
|
|
||||||
# var-never-set
|
|
||||||
## What it does
|
|
||||||
Identifies variables declared with `var` that are never modified with `set`.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
If a `var` is never modified, it should be declared with `local` or `let` instead
|
|
||||||
for clarity.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(var x 10)
|
|
||||||
(print x)
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(let [x 10]
|
|
||||||
(print x))
|
|
||||||
```
|
|
||||||
|
|
||||||
# op-with-no-arguments
|
|
||||||
## What it does
|
|
||||||
Warns when an operator is called with no arguments, which can be replaced with
|
|
||||||
an identity value.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Calling operators with no arguments is less clear than using the identity value
|
|
||||||
directly.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(+) ; Returns 0
|
|
||||||
(*) ; Returns 1
|
|
||||||
(..) ; Returns ""
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
0
|
|
||||||
1
|
|
||||||
""
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
This lint isn't actually very useful.
|
|
||||||
|
|
||||||
# no-decreasing-comparison (off by default)
|
|
||||||
## What it does
|
|
||||||
Suggests using increasing comparison operators (`<`, `<=`) instead of decreasing ones (`>`, `>=`).
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Consistency in comparison direction makes code more readable and maintainable,
|
|
||||||
especially in languages with lisp syntax. You can think of `<` as a function that
|
|
||||||
tests if the arguments are in sorted order.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(> a b)
|
|
||||||
(>= x y z)
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(< b a)
|
|
||||||
(<= z y x)
|
|
||||||
```
|
|
||||||
|
|
||||||
# match-should-case
|
|
||||||
## What it does
|
|
||||||
Suggests using `case` instead of `match` when the meaning would not be altered.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
The `match` macro's meaning depends on the local variables in scope. When a
|
|
||||||
`match` call doesn't use the local variables, it can be replaced with the
|
|
||||||
`case` form.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(match value
|
|
||||||
10 "ten"
|
|
||||||
20 "twenty"
|
|
||||||
_ "other")
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(case value
|
|
||||||
10 "ten"
|
|
||||||
20 "twenty"
|
|
||||||
_ "other")
|
|
||||||
```
|
|
||||||
|
|
||||||
# inline-unpack
|
|
||||||
## What it does
|
|
||||||
Warns when multiple values from `values` or `unpack` are used in a non-final
|
|
||||||
position of a function call, where only the first value will be used.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
In Fennel (and Lua), multiple values are only preserved when they appear in the
|
|
||||||
final position of a function call. Using them elsewhere results in only the
|
|
||||||
first value being used. This is likely not what was intended, since the use of
|
|
||||||
`values` or `unpack` seems to imply that the code is interested in handling
|
|
||||||
multivals instead of discarding them.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(print (values 1 2 3) 4) ; confusingly prints "1 4"
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
;; Try putting the multival at the end:
|
|
||||||
(print 4 (values 1 2 3))
|
|
||||||
|
|
||||||
;; Try writing the logic out manually instead of using multival
|
|
||||||
(let [(a b c) (values 1 2 3)]
|
|
||||||
(print a b c 4)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
It doesn't make sense to flag *all* places where a multival is discarded, because
|
|
||||||
discarding extra values is common in Lua. For example, in the standard library
|
|
||||||
of Lua, `string.gsub` and `require` actually return two results, even though
|
|
||||||
most of the time, only the first one is what's wanted.
|
|
||||||
|
|
||||||
This lint specifically flags discarding multivals from `values` and `unpack`,
|
|
||||||
instead of flagging all discards, because these forms indicate that the user
|
|
||||||
*intends* for something to happen with multivals.
|
|
||||||
|
|
||||||
You find more information about Lua's multivals in [Benaiah's excellent post explaining Lua's multivals](https://benaiah.me/posts/everything-you-didnt-want-to-know-about-lua-multivals),
|
|
||||||
or by searching the word "adjust" in the [Lua Manual](https://www.lua.org/manual/5.4/manual.html#3.4.12).
|
|
||||||
|
|
||||||
# empty-let
|
|
||||||
## What it does
|
|
||||||
Warns about `(let [] ...)` that should be `(do ...)`.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Using `let` with no bindings is unnecessarily verbose when `do` serves the same purpose more clearly.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(let []
|
|
||||||
(print "hello")
|
|
||||||
(print "world"))
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(do
|
|
||||||
(print "hello")
|
|
||||||
(print "world"))
|
|
||||||
```
|
|
||||||
|
|
||||||
# mismatched-argument-count (off by default)
|
|
||||||
## What it does
|
|
||||||
Checks if function calls have the correct number of arguments based on the function's signature.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Calling functions with the wrong number of arguments can lead to runtime errors
|
|
||||||
or unexpected behavior. This lint helps catch these issues early.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
(string.sub "hello") ; missing required arguments
|
|
||||||
(string.sub "hello" 1 2 3) ; too many arguments
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
(string.sub "hello" 1) ; provide all required arguments
|
|
||||||
(string.sub "hello" 1 2) ; remove extra arguments
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
This lint is disabled by default because it can produce false positives.
|
|
||||||
It assumes that all optional arguments are reliably annotated with a ? sigil,
|
|
||||||
and any other arguments can be assumed to be required. This is reasonably
|
|
||||||
accurate if the code follows Fennel conventions. Also this lint is very new and
|
|
||||||
may have issues, so I'd like to let people try it on their own terms before
|
|
||||||
enabling it by default.
|
|
||||||
|
|
||||||
In the future I may split it into "too-many-arguments" (which is accurate regardless of code style)
|
|
||||||
and "not-enough-arguments" (which needs the arglist to be annotated properly)
|
|
||||||
|
|
||||||
# duplicate-table-keys
|
|
||||||
## What it does
|
|
||||||
Detects when the same key appears multiple times in a table literal.
|
|
||||||
|
|
||||||
## Why is this bad?
|
|
||||||
Duplicate keys in a table are usually a mistake and the later value will
|
|
||||||
overwrite the earlier one, which can lead to bugs.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
```fnl
|
|
||||||
{:name "Alice"
|
|
||||||
:age 25
|
|
||||||
:name "Bob"} ; "Alice" gets overwritten by "Bob"
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, use:
|
|
||||||
```fnl
|
|
||||||
{:name "Bob"
|
|
||||||
:age 25}
|
|
||||||
```
|
|
||||||
|
|
||||||
@ -21,11 +21,18 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
|
|
||||||
(local all-lints [])
|
(local all-lints [])
|
||||||
|
|
||||||
(fn add-lint [code lint ...]
|
(fn add-lint [name lint ...]
|
||||||
|
(when (not= (type lint.what-it-does) "string") (error (.. name " needs a description of what it does in :what-it-does")))
|
||||||
|
(when (not= (type lint.why-care?) "string") (error (.. name " needs a description of why the linted pattern is bad in :why-care?")))
|
||||||
|
(when (not= (type lint.example) "string") (error (.. name " needs an example of broken and fixed code in :example")))
|
||||||
|
(when (not= (type lint.since) "string") (error (.. name " needs version: :since " (view utils.version))))
|
||||||
|
(when (= nil lint.type) (error (.. name " needs a type. available types: " (view (icollect [k (pairs lints)] k)))))
|
||||||
|
;; lint.limitations is optional
|
||||||
|
|
||||||
(table.insert all-lints lint)
|
(table.insert all-lints lint)
|
||||||
(for [i 1 (select :# lint ...)]
|
(for [i 1 (select :# lint ...)]
|
||||||
(let [lint (select i lint ...)]
|
(let [lint (select i lint ...)]
|
||||||
(set lint.name code)
|
(set lint.name name)
|
||||||
(if (= (type lint.type) :table)
|
(if (= (type lint.type) :table)
|
||||||
(each [_ t (ipairs lint.type)]
|
(each [_ t (ipairs lint.type)]
|
||||||
(table.insert (assert (. lints t) (.. "unknown lint type " t)) lint))
|
(table.insert (assert (. lints t) (.. "unknown lint type " t)) lint))
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
(faith.= ?response-string (?. message :result :contents :value)
|
(faith.= ?response-string (?. message :result :contents :value)
|
||||||
(.. "Invalid hover message\nfrom: " (view file-contents)))
|
(.. "Invalid hover message\nfrom: " (view file-contents)))
|
||||||
(= (type ?response-string) :function)
|
(= (type ?response-string) :function)
|
||||||
(faith.is (?response-string (?. message :result :contents :value))
|
(faith.is (-?> (?. message :result :contents :value) ?response-string)
|
||||||
(.. "Invalid hover message:\n"
|
(.. "Invalid hover message:\n"
|
||||||
(?. message :result :contents :value)
|
(or (?. message :result :contents :value) "nil")
|
||||||
"\nfrom: " (view file-contents)))
|
"\nfrom: " (view file-contents)))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user