The partdata module provides information about objects in game, such as what configurables the object has, what 'programmable events' it has, default size, durability and many other facts.

Here is the structure, written using [Luau's type system](https://luau.org/typecheck):

    type FaceOpacities = {
        [Enum.NormalId]: {
            Internal: "Push" | "Pull" | "Opaque",
            External: "Push" | "Pull" | "Opaque",
        } | "Exit" | "Enter",

    type ClassType = "Natural" | "Craftable" | "Tool" | "Unused"
    type Category = "Logic" | "Tools" | "Weapons" | "Electrical" | "Templates" | "Propulsion" | "Resources" | string
    type ClassState = "Solid" | "Liquid" | "Gas"

    type ConfigValue = {
        Name: string,
        Description: string,
    } & ({
        Type: string,
        [string]: any,
        Type: "number",
        Default: number,
        Options: { number },
        Type: "boolean",
        Default: boolean,
        Type: "string",
        Default: string,
        Type: "Selection",
        Default: number?,
        Options: { string },
    })

    type ComponentInfo = {
        -- Metadata
        ClassName: string,
        Description: string?,

        ConfigData: { ConfigValue }?,

        -- Behaviour
        Conflicts: { string }?,
        Events: { string }?,

    type PartInfo = {
        -- Metadata
        ClassName: string,
        Categories: { Category },
        ClassState: ClassState,
        ClassType: ClassType,
        Description: string?,
        Color: Color3?,

        Recipe: { [string]: number }?,
        ConfigData: { ConfigValue }?,

        Spawnable: boolean,
        Craftable: boolean,

        -- Physical properties
        Malleability: number,
        BaseSize: Vector3,
        BaseDurability: number,
        BaseHeatCapacity: number,

        -- Behaviour
        Flammable: boolean,
        FaceOpacity: FaceOpacities?,
        Events: { string }?,
        ProgrammableEvents: { string }?,

        ResourceType: string?,
        ResourceAmount: number?,

        GeneratorType: string?,
        GeneratorAmount: number?,

        HeatPowerProduct: number?,
        HeatGeneratorProduct: number?,

    type partdata = {
        Components: { [string]: ComponentInfo },
        Parts: { [string]: PartInfo },


Please note you can find the raw documentation [here](https://arvidsilverlock.github.io/Pilot.lua-Luau-LSP/raw-documentation), I know it is hard to read, but you should be able to find any information you need in there.

This page is manually transcribed from the data and is not automatically generated.

To retrieve with physical parts, use the [`Network`](https://arvidsilverlock.github.io/Pilot.lua-Luau-LSP/types/Network) library (it is accessed through the `Network` global).

Here are a list of global functions:

  * **JSONDecode**(_string_ data) → _JSON_
    * Decodes JSON. Proxy for `HttpService:JSONDecode`
  * **JSONEncode**(_JSON_ data) → _string_
    * Encodes JSON. Proxy for `HttpService:JSONEncode`
  * **GetCPUTime**() → _number_
    * Returns the current elapsed CPU time.
  * **logError**(_string_ message, _number?_ level)
    * Logs an error in the output without yielding execution.

These functions will be removed in future:

  * **TriggerPort**(_Port | number_ port)
    * Triggers the specified port.
  * **Beep**(_number?_ frequency)
    * Produces a beep sound using a triangle wave.


Creating rigidbodies in the engine is fairly simple, just configure the engine and use `Engine:Create()` method! When a RigidBody is created, a unique ID is assigned to it, Points are created for its vertices and these points are connected with Constraints!

RigidBodies support the following UI elements.

  * Frame
  * ScrollingFrame
  * ImageButton
  * ImageLabel
  * TextLabel
  * TextBox
  * TextButton
  * VideoFrame
  * ViewportFrame

In this example, we create the following simulation:

The first step would be the create a frame which will be an anchored RigidBody.

Next we'll create separate ImageLabels for each box, you can do this using a script as well! For the boxes, I used the following image:

I spread the boxes around and group them inside a single folder!

Let's bring our boxes to life! Firstly we start by initializing our engine.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Nature2D = require(ReplicatedStorage:FindFirstChild("Nature2D"))

    local ScreenGui = script.Parent
    local Canvas = ScreenGui.Canvas

    local engine = Nature2D.init(ScreenGui)

Copy

Next we make the Anchored base an anchored rigidbody!

    engine:Create("RigidBody", {
        Object = Canvas.AnchoredPart,
        Collidable = true,
        Anchored = true
    })

Copy

We now loop through each box in the 'Boxes' folder and make it an unanchored rigid body! Lastly, we start the engine and boom! You have the simulation ready.

    for _, box in ipairs(Canvas.Boxes:GetChildren()) do
        engine:Create("RigidBody", {
            Object = box,
            Collidable = true,
            Anchored = false
        })
    end

    engine:Start()

Copy

This is how easily you can create RigidBodies for your games with potentially any UI element. You can try replacing them with textboxes, viewportframes etc!


[ ](https://twitter.com/jaipack17) [ ](https://github.com/jaipack17) [ ](https://www.roblox.com/users/1659338449/profile) [ ](https://editor.p5js.org/jaipack17/sketches)

# Jaikaran Singh

Programmer ・ Game and Web Developer ・ Physics Enthusiast

[ New Voyage of Code  ](https://jaipack17.github.io/voyage.html "Visit Blog")

Hi! I'm jaipack17. I'm a programmer, web and game developer. I love physics and mathematics! I create and contribute to many open-sourced projects and, write articles on game development, physics and mathematics. Nowadays learning more about machine learning.
See you around!

# Technologies I Use

# Projects

# Nature2D

A custom 2D physics engine for GUIs on Roblox. Equipped with collisions, RigidBodies and Constraints.

[Github](https://github.com/jaipack17/Nature2D) ・ [DevForum](https://devforum.roblox.com/t/physics-library-nature2d-bring-ui-elements-to-life/1510935) ・ [Website](https://jaipack17.github.io/Nature2D)

# Physics Based 2D Games

A collection of my 2D Game mechanics and physics on Roblox, merely made with Guis exclusive of 3D objects.

[Roblox](https://www.roblox.com/users/1659338449/profile)

# GuiCollisionService

A Lua module to help detect collisions between quadrilaterals with different their orientations.

[Github](https://github.com/jaipack17/GuiCollisionService) ・ [DevForum](https://devforum.roblox.com/t/v30-guicollisionservice-gui-collisions-done-right/1370948)

# GuiPivot

Rotation Pivots for Guis on Roblox.

[Github](https://gist.github.com/jaipack17/653c3f5c0a11a6fe0b151aced5ca451a)

# Roblox BST

Binary Search Tree (BST) class for Roblox in lua.

[Github](https://github.com/jaipack17/roblox-tree)

# Stack

Stack data-structure class for Roblox in lua.

[Github](https://github.com/jaipack17/BetterStackLua)

# ruxe

A javascript module that helps you turn long strings into neat paragraphs.

[Github](https://github.com/jaipack17/ruxe)

# cli-pages

A javascript module that helps you create paginated books for your command line apps.

[Github](https://github.com/jaipack17/cli-pages)

# RayCast2

RayCasting for GUIs on Roblox.

[Github](https://github.com/jaipack17/RayCast2) ・ [DevForum](https://devforum.roblox.com/t/raycast2-cast-rays-on-a-2d-surface/1430643) ・ [Website](https://jaipack17.gitbook.io/raycast2/)

# 2D Terrain

A module for procedurally generating 2D colored terrain using perlin noise.

[Github](https://github.com/jaipack17/2dterrain) ・ [DevForum](https://devforum.roblox.com/t/release-2d-terrain-generation-using-perlin-noise/1412013)

# 2D Rope Swinging

Physics based rope swinging mechanic for 2D Gui based Roblox games.

[DevForum](https://devforum.roblox.com/t/gui-based-rope-swinging-physics-tarzan/1453563)

# Gizmo2D

Visual Debugging library for GUIs on Roblox.

[Github](https://github.com/jaipack17/Gizmo2D)

# Articles

# Optimizing Curves Algorithmically

A few techniques and algorithms for curve and polyline simplification

[Read Article](https://github.com/jaipack17/write-ups/tree/main/Optimizing%20Curves%20Algorithmically#readme)

# 2D Collisions

An article on the fundamentals and techniques of 2D Collision detection and response.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/2D%20Collisions)

# Verlet Integration

The beauty of Verlet Integration and its implementation on Roblox.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/Verlet%20Integration)

# Quadtrees

Quadtrees, how they work, how they are more performant, implementation in lua as well as collision detection.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/Implementing%20and%20Using%20Quadtrees)

# Bézier Curves

Understanding and implementing Bézier Curves in Roblox using Linear Interpolation.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/B%C3%A9zier%20Curves)

# Steering Behaviors

An article that covers everything of almost all the Steering Behaviors developed by Craig Reynolds.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/Steering%20Behaviors)

# 2D Oscillating Pendulum

An introduction to angular motion. Implementation of a 2D Oscillating Pendulum on Roblox.

[Read Article](https://devforum.roblox.com/t/gui-pendulum-oscillatory-motion-in-a-2d-space/1406576)

# Simulating Physics with GUIs

An introduction to simulating physics with GUIs on Roblox, where I go through the simulation of forces and collisions.

[Read Article](https://devforum.roblox.com/t/apple-falling-on-newtons-head-simulating-physics-using-guis/1443977)

# Vector Fields

INCOMPLETE. An article on Static vector fields, the concept of fluid flow, curl and divergence.

[Read Article](https://github.com/jaipack17/write-ups/tree/main/Vector%20Fields)

© 2022 jaipack17


Here are some quick links to get started using Promises:

  * [Installation guide](https://eryn.io/roblox-lua-promise/docs/Installation)
  * [**API Docs**](https://eryn.io/roblox-lua-promise/api/Promise)
  * [Tour of Promises](https://eryn.io/roblox-lua-promise/docs/Tour)
  * [Why use Promises at all?](https://eryn.io/roblox-lua-promise/docs/WhyUsePromises)
  * [Examples](https://eryn.io/roblox-lua-promise/docs/Examples)


# selene

**selene** is a command line tool designed to help write correct and idiomatic Lua code.

  * **New to selene?** Read the [motivation behind its creation](https://kampfkarren.github.io/selene/motivation.html) and the [CLI guide](https://kampfkarren.github.io/selene/cli/index.html). If you already use [Luacheck](https://luacheck.readthedocs.io/en/stable/), you can read the [Luacheck comparison and migration guide](https://kampfkarren.github.io/selene/luacheck.html).
  * **Don't know what a linter is?** Check out [the wiki on linters](https://en.wikipedia.org/wiki/Lint_\(software\)) about what a linter does and how it's beneficial.
  * **Existing user?** Read the [changelog](https://github.com/Kampfkarren/selene/blob/master/CHANGELOG.md) to learn about any new features or bug fixes.
  * **Interested in what selene lints for?** Read the [list of lints](https://kampfkarren.github.io/selene/lints/index.html).
  * **Interested in contributing to selene's codebase?** Read the [contribution guide](https://kampfkarren.github.io/selene/contributing.html).
  * **Want to discuss selene or get help?** Join the [Roblox OS Community Discord](https://discord.gg/mhtGUS8). Note: selene is not Roblox exclusive, but it started its life for Roblox development.

## License

selene and all its source code are licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/).

# Motivation

## Because bugs

When writing any code, it's very easy to make silly mistakes that end up introducing bugs. A lot of the time, these bugs are hard to track down and debug, and sometimes are even harder to replicate.

This risk is made ever more real because of the generally lax nature of Lua. Incorrect code is regularly passed off and isn't noticed until something breaks at runtime. Sometimes you'll get a clear error message, and will have to spend time going back, fixing the code, and making sure you actually fixed it. Other times, the effects are more hidden, and instead of getting an error your code will just pass through along, in an incorrect state.

Take, for example, this code:

    function Player:SwapWeapons()
        self.CurrentWeapon = self.SideWeapon
        self.SideWeapon = self.CurrentWeapon
    end

This is code that is technically correct, but is absolutely not what you wanted to write. However, because it is _technically_ correct, Lua will do exactly what you tell it to do, and so...

  * Player wants to swap their weapons
  * Your code calls `player:SwapWeapons()`
  * Their current weapon is set to their side weapon...
  * ...but their side weapon is set to their current weapon afterwards, which is what they just equipped!

Uh oh! After debugging this, you realize that you actually meant to write was...

    function Player:SwapWeapons()
        self.CurrentWeapon, self.SideWeapon = self.SideWeapon, self.CurrentWeapon
    end

If you were using selene, you would've been alerted right away that your original code looked like an [`almost_swapped`](https://kampfkarren.github.io/selene/lints/almost_swapped.html).

    error[almost_swapped]: this looks like you are trying to swap `self.CurrentWeapon` and `self.SideWeapon`

       ┌── fail.lua:4:5 ───
       │
     4 │ ╭     self.CurrentWeapon = self.SideWeapon
     5 │ │     self.SideWeapon = self.CurrentWeapon
       │ ╰────────────────────────────────────────^
       │
       = try: `self.CurrentWeapon, self.SideWeapon = self.SideWeapon, self.CurrentWeapon`

Other bugs arise because of Lua's lack of typing. While it can feel freeing to developers to not have to specify types everywhere, it makes it easier to mess up and write broken code. For example, take the following code:

    for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do

This code is yet again technically correct, but not what we wanted to do. `pairs` will take the first argument, `GoldShop`, and ignore the rest. Worse, the `shop` variable will now be the values of the contents of `GoldShop`, not the shop itself. This can cause massive headaches, since although you're likely to get an error later down the line, it's more likely it'll be in the vein of "attempt to index a nil value `items`" than something more helpful. If you used `ipairs` instead of `pairs`, your code inside might just not run, and won't produce an error at all.

Yet again, selene saves us.

    error[incorrect_standard_library_use]: standard library function `pairs` requires 1 parameters, 3 passed

       ┌── fail.lua:1:16 ───
       │
     1 │ for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do
       │                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       │

This clues the developer into writing the code they meant to write:

    for _, shop in pairs({ GoldShop, ItemShop, MedicineShop }) do

### Because idiomatic Lua

While it's nice to write code however you want to, issues can arise when you are working with other people, or plan on open sourcing your work for others to contribute to. It's best for everyone involved if they stuck to the same way of writing Lua.

Consider this contrived example:

    call(1 / 0)

The person who wrote this code might have known that `1 / 0` evaluates to `math.huge`. However, anyone working on that code will likely see it and spend some time figuring out why they wrote the code that way.

If the developer was using selene, this code would be denied:

    warning[divide_by_zero]: dividing by zero is not allowed, use math.huge instead

       ┌── fail.lua:1:6 ───
       │
     1 │ call(1 / 0)
       │      ^^^^^
       │

Furthermore, selene is meant to be easy for developers to add their own lints to. You could create your own lints for your team to prevent behavior that is non-idiomatic to the codebase. For example, let's say you're working on a [Roblox](https://developer.roblox.com/en-us) codebase, and you don't want your developers using the data storage methods directly. You could create your own lint so that this code:

    local DataStoreService = game:GetService("DataStoreService")

...creates a warning, discouraging its use. For more information on how to create your own lints, check out the [contribution guide](https://kampfkarren.github.io/selene/contributing.html).

# Luacheck Comparison

## selene vs. luacheck

selene is not the first Lua linter. The main inspiration behind selene is [luacheck](https://luacheck.readthedocs.io/en/stable/). However, the two have very little in common besides inception.

  * selene is actively maintained, while at the time of writing luacheck's last commit was in October 2018.
  * selene is written in Rust, while luacheck is written in Lua. In practice, this means that selene is much faster than luacheck while also being able to easily take advantage of features luacheck cannot because of the difficulty of using dependencies in Lua.
  * selene is multithreaded, again leading to significantly better performance.
  * selene has rich output, while luacheck has basic output.

selene:

    error[suspicious_reverse_loop]: this loop will only ever run once at most

       ┌── fail.lua:1:9 ───
       │
     1 │ for _ = #x, 1 do
       │         ^^^^^
       │
       = help: try adding `, -1` after `1`

luacheck:

    Checking fail.lua                                 2 warnings

        fail.lua:1:1: numeric for loop goes from #(expr) down to 1 but loop step is not negative

  * selene uses [TOML](https://github.com/toml-lang/toml) files for configuration, while luacheck uses `.luacheckrc`, which runs Lua.
  * selene allows for [standard library configuration](https://kampfkarren.github.io/selene/usage/std.html) such as argument types, argument counts, etc, while luacheck only allows knowing that fields exist and can be written to. In practice, this means that selene catches:

    for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do

    math.pi()

...while luacheck does not.

  * selene has English names for lints instead of arbitrary numbers. In luacheck, you ignore "[`211`](https://luacheck.readthedocs.io/en/stable/warnings.html#unbalanced-assignments)", while in selene you ignore "[`unbalanced_assignments`](https://kampfkarren.github.io/selene/lints/unbalanced_assignments.html)".
  * selene has distinctions for "deny" and "warn", while every luacheck lint is the same.
  * selene has a much simpler codebase, and is much easier to add your own lints to.
  * selene has optional support and large focus specifically for Roblox development.
  * selene will only show you files that lint, luacheck only does this with the `-q` option (quiet).
  * selene [filters specific lints](https://kampfkarren.github.io/selene/usage/filtering.html) and applies over code rather than lines, luacheck does not.
  * selene has [significantly more lints](https://kampfkarren.github.io/selene/lints/index.html).

This is not to say selene is objectively better than luacheck, at least not yet.

  * luacheck has lints for long lines and whitespace issues, selene does not as it is unclear whether style issues like these are fit for a linter or better under the scope of a Lua beautifier.
  * luacheck officially supports versions past Lua 5.1, selene does not yet as there is not much demand.
  * luacheck supports the following lints that selene does not yet:
    * Unreachable code
    * Unused labels (selene does not officially support Lua 5.2 yet)
    * Detecting variables that are only ever mutated, but not read
    * Using uninitialized variables

## Migration

luacheck does not require much configuration to begin with, so migration should be easy.

  * You can configure what lints are allowed in the [configuration](https://kampfkarren.github.io/selene/usage/configuration.html#changing-the-severity-of-lints).
  * Do you have a custom standard library (custom globals, functions, etc)? Read the [standard library guide](https://kampfkarren.github.io/selene/usage/std.html).
    * Are you a Roblox developer using something like [luacheck-roblox](https://github.com/Quenty/luacheck-roblox/)? A featureful standard library for Roblox is generated with every commit on GitHub. TODO: Have a flag in the selene CLI to generate a Roblox standard library a la `generate-roblox-std`? Should `generate-roblox-std` be uploaded to crates.io?

# Command Line Interface

**selene** is mostly intended for use as a command line tool.

In this section, you will learn how to use selene in this manner.

# Installation

**selene** is written in Rust, and the recommended installation method is through the [Cargo package manager](https://doc.rust-lang.org/cargo/).

To use Cargo, you must first install Rust. [rustup.rs](https://rustup.rs/) is a tool that makes this very easy.

Once you have Rust installed, use either command:

**If you want the most stable version of selene**

    cargo install selene

**If you want the most up to date version of selene**

    cargo install --branch main --git https://github.com/Kampfkarren/selene selene

### Disabling Roblox features

selene is built with Roblox specific lints by default. If you don't want these, then pass `--no-default-features` to the `cargo install` command.

# CLI Usage

If you want to get a quick understanding of the interface, simply type `selene --help`.

    USAGE:
        selene [FLAGS] [OPTIONS] <files>...
        selene <SUBCOMMAND>

    FLAGS:
            --allow-warnings    Pass when only warnings occur
            --no-exclude        Ignore excludes defined in config
        -h, --help              Prints help information
        -n, --no-summary        Suppress summary information
        -q, --quiet             Display only the necessary information. Equivalent to --display-style="quiet"
        -V, --version           Prints version information

    OPTIONS:
            --color <color>                     [default: auto]  [possible values: Always, Auto, Never]
            --config <config>                  A toml file to configure the behavior of selene [default: selene.toml]
            --display-style <display-style>    Sets the display method [possible values: Json, Json2, Rich, Quiet]
            --num-threads <num-threads>        Number of threads to run on, default to the numbers of logical cores on your
                                               system [default: your system's cores]
            --pattern <pattern>                A glob to match files with to check

    ARGS:
        <files>...

    SUBCOMMANDS:
        generate-roblox-std
        help                   Prints this message or the help of the given subcommand(s)
        update-roblox-std
        upgrade-std

## Basic usage

All unnamed inputs you give to selene will be treated as files to check for.

If you want to check a folder of files: `selene files`

If you just want to check one file: `selene code.lua`

If you want to check multiple files/folders: `selene file1 file2 file3 ...`

If you want to pipe code to selene using stdin: `cat code.lua | selene -`

## Advanced options

**-q**

**--quiet**

Instead of the rich format, only necessary information will be displayed.

    ~# selene code.lua
    warning[divide_by_zero]: dividing by zero is not allowed, use math.huge instead

       ┌── code.lua:1:6 ───
       │
     1 │ call(1 / 0)
       │      ^^^^^
       │

    Results:
    0 errors
    1 warnings
    0 parse errors

    ~# selene code.lua -q
    code.lua:1:6: warning[divide_by_zero]: dividing by zero is not allowed, use math.huge instead

    Results:
    0 errors
    1 warnings
    0 parse errors

**--num-threads** _num-threads_

Specifies the number of threads for selene to use. Defaults to however many cores your CPU has. If you type `selene --help`, you can see this number because it will show as the default for you.

**--pattern** _pattern_

A [glob](https://en.wikipedia.org/wiki/Glob_\(programming\)) to match what files selene should check for. For example, if you only wanted to check files that end with `.spec.lua`, you would input `--pattern **/*.spec.lua`. Defaults to `**/*.lua`, meaning "any lua file", or `**/*.lua` and `**/*.luau` with the roblox feature flag, meaning "any lua/luau file".

# Usage

In this section, you will learn how to interact with selene from your code and how to fit it to your liking.

# Configuration

selene is meant to be easily configurable. You can specify configurations for the entire project as well as for individual lints.

Configuration files are placed in the directory you are running selene in and are named **selene.toml**. As the name suggests, the configurations use the [Tom's Obvious, Minimal Language (TOML)](https://github.com/toml-lang/toml) format. It is recommended you quickly brush up on the syntax, though it is very easy.

## Changing the severity of lints

You can change the severity of lints by entering the following into selene.toml:

    [lints]
    lint_1 = "severity"
    lint_2 = "severity"
    ...

Where "severity" is one of the following:

  * `"allow"` \- Don't check for this lint
  * `"warn"` \- Warn for this lint
  * `"deny"` \- Error for this lint

Note that "deny" and "warn" are effectively the same, only warn will give orange text while error gives red text, and they both have different counters.

## Configuring specific lints

You can configure specific lints by entering the following into selene.toml:

    [config]
    lint1 = ...
    lint2 = ...
    ...

Where the value is whatever the special configuration of that lint is. You can learn these on the lints specific page in the [list of lints](https://kampfkarren.github.io/selene/lints/index.html). For example, if we wanted to allow empty if branches if the contents contain comments, then we would write:

    [config]
    empty_if = { comments_count = true }

## Setting the standard library

Many lints use standard libraries for either verifying their correct usage or for knowing that variables exist where they otherwise wouldn't.

By default, selene uses Lua 5.1, though if we wanted to use the Lua 5.2 standard library, we would write:

    std = "lua52"

...at the top of selene.toml. You can learn more about the standard library format on the [standard library guide](https://kampfkarren.github.io/selene/usage/std.html). The standard library given can either be one of the builtin ones (currently only `lua51` and `lua52`) or the filename of a standard library file in this format. For example, if we had a file named `special.toml`, we would write:

    std = "special"

### Chaining the standard library

We can chain together multiple standard libraries by simply using a plus sign (`+`) in between the names.

For example, if we had `game.toml` and `engine.toml` standard libraries, we could chain them together like so:

    std = "game+engine"

### Excluding files from being linted

It is possible to exclude files from being linted using the exclude option:

    exclude = ["external/*", "*.spec.lua"]

# Filtering

Lints can be toggled on and off in the middle of code when necessary through the use of special comments.

## Allowing/denying lints for a piece of code

Suppose we have the following code:

    local something = 1

selene will correctly attribute this as an unused variable:

    warning[unused_variable]: something is assigned a value, but never used

       ┌── code.lua:1:7 ───
       │
     1 │ local something = 1
       │       ^^^^^^^^^
       │

However, perhaps we as the programmer have some reason for leaving this unused (and not renaming it to `_something`). This would be where inline lint filtering comes into play. In this case, we would simply write:

    -- selene: allow(unused_variable)
    local something = 1

This also works with settings other than `allow`\--you can `warn` or `deny` lints in the same fashion. For example, you can have a project with the following `selene.toml` [configuration](https://kampfkarren.github.io/selene/usage/configuration.html):

    [lints]
    unused_variable = "allow" # I'm fine with unused variables in code

...and have this in a separate file:

    -- I'm usually okay with unused variables, but not this one
    -- selene: deny(unused_variable)
    local something = 1

This is applied to the entire piece of code its near, _not_ just the next line. For example:

    -- selene: allow(unused_variable)
    do
        local foo = 1
        local bar = 2
    end

...will silence the unused variable warning for both `foo` and `bar`.

## Allowing/denying lints for an entire file

If you want to allow/deny a lint for an entire file, you can do this by attaching the following code to the beginning:

    --# selene: allow(lint_name)

The `#` tells selene that you want to apply these globally.

These _must_ be before any code, otherwise selene will deny it. For example, the following code:

    local x = 1
    --# selene: allow(unused_variable)

...will cause selene to error:

    warning[unused_variable]: x is assigned a value, but never used
      ┌─ -:1:7
      │
    1 │ local x = 1
      │       ^

    error[invalid_lint_filter]: global filters must come before any code
      ┌─ -:1:1
      │
    1 │ local x = 1
      │ ----------- global filter must be before this
    2 │ --# selene: allow(unused_variable)

## Combining multiple lints

You can filter multiple lints in two ways:

    -- selene: allow(lint_one)
    -- selene: allow(lint_two)

    -- or...

    -- selene: allow(lint_one, lint_two)

# Standard Library Format

selene provides a robust standard library format to allow for use with environments other than vanilla Lua. Standard libraries are defined in the form of [YAML](https://en.wikipedia.org/wiki/YAML) files.

## Examples

For examples of the standard library format, see:

  * [`lua51.yml`](https://github.com/Kampfkarren/selene/blob/main/selene-lib/default_std/lua51.yml) \- The default standard library for Lua 5.1
  * [`lua52.yml`](https://github.com/Kampfkarren/selene/blob/main/selene-lib/default_std/lua52.yml) \- A standard library for Lua 5.2's additions and removals. Reference this if your standard library is based off another (it most likely is).
  * [`roblox.yml`](https://gist.github.com/Kampfkarren/dff2dc17cc30d68a48510da58fff2381) \- A standard library for Roblox that incorporates all the advanced features of the format. If you are a Roblox developer, don't use this as anything other than reference--an up to date version of this library is automatically generated.

## base

Used for specifying what standard library to be based off of. This supports both builtin libraries (lua51, lua52, lua53, lua54, roblox), as well as any standard libraries that can be found in the current directory.

    --- # This begins a YAML file
    base: lua51 # We will be extending off of Lua 5.1.

## lua_versions

Used for specifying the versions of Lua you support for the purpose of supporting the syntax of those dialects. If empty, will default to 5.1.

Supports the following options:

  * `lua51` \- Lua 5.1
  * `lua52` \- Lua 5.2
  * `lua53` \- Lua 5.3
  * `lua54` \- Lua 5.4
  * `luau` \- [Luau](https://luau-lang.org)
  * `luajit` \- LuaJIT

Usually you only need to specify one--for example, `lua54` will give Lua 5.4 syntax and all versions prior. That means that if you specify it, it will look something like:

    lua_versions:
    - luajit

If you are extending off a library that specifies it (like `lua51`, etc) then you do not need this. If you specify it while overriding a library, it will override it.

## globals

This is where the magic happens. The `globals` field is a dictionary where the keys are the globals you want to define. The value you give tells selene what the value can be, do, and provide.

If your standard library is based off another, overriding something defined there will use your implementation over the original.

### Any

    globals:
      foo:
        any: true

This specifies that the field can be used in any possible way, meaning that `foo.x`, `foo:y()`, etc will all validate.

### Functions

    globals:
      tonumber:
        args:
          - type: any
          - type: number
            required: false

A field is a function if it contains an `args` and/or `method` field.

If `method` is specified as `true` and the function is inside a table, then it will require the function be called in the form of `Table:FunctionName()`, instead of `Table.FunctionName()`.

`args` is an array of arguments, in order of how they're used in the function. An argument is in the form of:

    required?: false | true | string;
    type: "any" | "bool" | "function" | "nil"
        | "number" | "string" | "table" | "..."
        | string[] | { "display": string }

#### "required"

  * `true` \- The default, this argument is required.
  * `false` \- This argument is optional.
  * A string - This argument is required, and not using it will give this as the reason why.

#### "observes"

This field is used for allowing smarter introspection of how the argument given is used.

  * "read-write" - The default. This argument is potentially both written to and read from.
  * "read" - This argument is only read from. Currently unused.
  * "write" - This argument is only written to. Used by `unused_variable` to assist in detecting a variable only being written to, even if passed into a function.

Example:

     table.insert:
      args:
        - type: table
          observes: write # This way, `table.insert(x, 1)` doesn't count as a read to `x`
        - type: any
        - required: false
          type: any

#### "must_use"

This field is used for checking if the return value of a function is used.

  * `false` \- The default. The return value of this function does not need to be used.
  * `true` \- The return value of this function must be used.

Example:

      tostring:
        args:
          - type: any
        must_use: true

#### Argument types

  * `"any"` \- Allows any value.
  * `"bool"`, `"function"`, `"nil"`, `"number"`, `"string"`, `"table"` \- Expects a value of the respective type.
  * `"..."` \- Allows any number of variables after this one. If `required` is true (it is by default), then this will lint if no additional arguments are given. It is incorrect to have this in the middle.
  * Constant list of strings - Will check if the value provided is one of the strings in the list. For example, `collectgarbage` only takes one of a few exact string arguments--doing `collectgarbage("count")` will work, but `collectgarbage("whoops")` won't.
  * `{ "display": string }` \- Used when no constant could possibly be correct. If a constant is used, selene will tell the user that an argument of the type (display) is required. For an example, the Roblox method `Color3.toHSV` expects a `Color3` object--no constant inside it could be correct, so this is defined as:

    globals:
      Color3.toHSV:
        args:
          - type:
              display: Color3

### Properties

    globals:
      _VERSION:
        property: read-only

Specifies that a property exists. For example, `_VERSION` is available as a global and doesn't have any fields of its own, so it is just defined as a property.

The same goes for `_G`, which is defined as:

    _G:
      property: new-fields

The value of property tells selene how it can be mutated and used:

  * `"read-only"` \- New fields cannot be added or set, and the variable itself cannot be redefined.
  * `"new-fields"` \- New fields can be added and set, but variable itself cannot be redefined. In the case of _G, it means that `_G = "foo"` is linted against.
  * `"override-fields"` \- New fields can't be added, but entire variable can be overridden. In the case of Roblox's `Instance.Name`, it means we can do `Instance.Name = "Hello"`, but not `Instance.Name.Call()`.
  * `"full-write"` \- New fields can be added and entire variable can be overridden.

### Struct

    globals:
      game:
        struct: DataModel

Specifies that the field is an instance of a [struct](https://kampfkarren.github.io/selene/usage/std.html#structs). The value is the name of the struct.

### Tables

    globals:
      math.huge:
        property: read-only
      math.pi:
        property: read-only

A field is understood as a table if it has fields of its own. Notice that `math` is not defined anywhere, but its fields are. This will create an implicit `math` with the property writability of `read-only`.

### Deprecated

Any field or arg can have a deprecation notice added to it, which will then be read by [the deprecated lint](https://kampfkarren.github.io/selene/lints/deprecated.html).

    globals:
      table.getn:
        args:
          - type: table
          - type: number
        deprecated:
          message: "`table.getn` has been superseded by #."
          replace:
            - "#%1"

The deprecated field consists of two subfields.

`message` is required, and is a human readable explanation of what the deprecation is, and potentially why.

`replace` is an optional array of replacements. The most relevant replacement is suggested to the user. If used with a function, then every parameter of the function will be provided.

For instance, since `table.getn`'s top replacement is `#%1`:

  * `table.getn(x)` will suggest `#x`
  * `table.getn()` will not suggest anything, as there is no relevant suggestion

You can also use `%...` to list every argument, separated by commas.

The following:

    globals:
      call:
        deprecated:
          message: "call will be removed in the next version"
          replace:
            - "newcall(%...)"
        args:
          - type: "..."
            required: false

...will suggest `newcall(1, 2, 3)` for `call(1, 2, 3)`, and `newcall()` for `call()`.

You can also use `%%` to write a raw `%`.

### Removed

    globals:
      getfenv:
        removed: true

Used when your standard library is [based off](https://kampfkarren.github.io/selene/usage/std.html#base) another, and your library removes something from the original.

## Structs

Structs are used in places such as Roblox Instances. Every Instance in Roblox, for example, declares a `:GetChildren()` method. We don't want to have to define this everywhere an Instance is declared globally, so instead we just define it once in a struct.

Structs are defined as fields of `structs`. Any fields they have will be used for instances of that struct. For example, the Roblox standard library has the struct:

    structs:
      Event:
        Connect:
          method: true
          args:
            - type: function

From there, it can define:

    globals:
      workspace.Changed:
        struct: Event

...and selene will know that `workspace.Changed:Connect(callback)` is valid, but `workspace.Changed:RandomNameHere()` is not.

## Wildcards

Fields can specify requirements if a field is referenced that is not explicitly named. For example, in Roblox, instances can have arbitrary fields of other instances (`workspace.Baseplate` indexes an instance named Baseplate inside `workspace`, but `Baseplate` is nowhere in the Roblox API).

We can specify this behavior by using the special `"*"` field.

    workspace.*:
      struct: Instance

This will tell selene "any field accessed from `workspace` that doesn't exist must be an Instance [struct](https://kampfkarren.github.io/selene/usage/std.html#structs)".

Wildcards can even be used in succession. For example, consider the following:

    script.Name:
      property: override-fields

    script.*.*:
      property: full-write

Ignoring the wildcard, so far this means:

  * `script.Name = "Hello"` _will_ work.
  * `script = nil` _will not_ work, because the writability of `script` is not specified.
  * `script.Name.UhOh` _will not_ work, because `script.Name` does not have fields.

However, with the wildcard, this adds extra meaning:

  * `script.Foo = 3` _will not_ work, because the writability of `script.*` is not specified.
  * `script.Foo.Bar = 3` _will_ work, because `script.*.*` has full writability.
  * `script.Foo.Bar.Baz = 3` _will_ work for the same reason as above.

## Internal properties

There are some properties that exist in standard library YAMLs that exist specifically for internal purposes. This is merely a reference, but these are not guaranteed to be stable.

### name

This specifies the name of the standard library. This is used internally for cases such as only giving Roblox lints if the standard library is named `"roblox"`.

### last_updated

A timestamp of when the standard library was last updated. This is used by the Roblox standard library generator to update when it gets too old.

### last_selene_version

A timestamp of the last selene version that generated this standard library. This is used by the Roblox standard library generator to update when it gets too old.

### roblox_classes

A map of every Roblox class and their properties, for [roblox_incorrect_roact_usage](https://kampfkarren.github.io/selene/lints/roblox_incorrect_roact_usage.html).

# Roblox Guide

selene is built with Roblox development in mind, and has special features for Roblox developers.

If you try to run selene on a Roblox codebase, you'll get a bunch of errors saying things such as "`game` is not defined". This is because these are Roblox specific globals that selene does not know about. You'll need to install the Roblox [standard library](https://kampfkarren.github.io/selene/usage/configuration.html) in order to fix these issues, as well as get Roblox specific lints.

## Installation

Thankfully, this process is very simple. All you need to do is edit your `selene.toml` (or create one) and add the following:

    std = "roblox"

The next time you run selene, or if you use the Visual Studio Code extension and start typing Lua code, a Roblox standard library will be automatically generated and used. This is an automatic process that occurs whenever you don't have a cached standard library file and your `selene.toml` has `std = "roblox"`.

## Updating definitions

The Roblox standard library file is updated automatically every 6 hours. If you need an update faster than that, you can run `selene update-roblox-std` manually.

## TestEZ Support

Roblox has provided an open source testing utility called [TestEZ](https://roblox.github.io/testez/), which allows you to write unit tests for your code. Writing unit tests is good practice, but selene will get angry at you if you don't include a `testez.yml` file and set the standard library to the following:

`std = "roblox+testez"`

But first you'll need to create a `testez.yml` file, which you can do so [with this template](https://gist.github.com/Kampfkarren/f2dddc2ebfa4e0662e44b8702e519c2d).

## Pinned standard library

There may be cases where you would rather not have selene automatically update the Roblox standard library, such as if speed is critically important and you want to limit potential internet access (generating the standard library requires an active internet connection).

selene supports "pinning" the standard library to a specific version.

Add the following to your `selene.toml` configuration:

    # `floating` by default, meaning it is stored in a cache folder on your system
    roblox-std-source = "pinned"

This will generate the standard library file into `roblox.yml` where it is run.

You can also create a `roblox.yml` file manually with `selene generate-roblox-std`.

# Contributing

selene is written in Rust, so knowledge of the ecosystem is expected.

selene uses [Full Moon](https://github.com/Kampfkarren/full-moon) to parse the Lua code losslessly, meaning whitespace and comments are preserved. You can read the full documentation for full-moon on its [docs.rs](https://docs.rs/full_moon/latest/full_moon/) page.

TODO: Upload selene-lib on crates.io and link the docs.rs page for that as well as throughout the rest of this article.

## Writing a lint

In selene, lints are created in isolated modules. To start, create a file in `selene-lib/src/lints` with the name of your lint. In this example, we're going to call the lint `cool_lint.rs`.

Let's now understand what a lint consists of. selene takes lints in the form of structs that implement the `Lint` trait. The `Lint` trait expects:

  * A `Config` associated type that defines what the configuration format is expected to be. Whatever you pass must be [deserializable](https://serde.rs/).
  * An `Error` associated type that implements [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html). This is used if configurations can be invalid (such as a parameter only being a number within a range). Most of the time, configurations cannot be invalid (other than deserializing errors, which are handled by selene), and so you can set this to [`std::convert::Infallible`](https://doc.rust-lang.org/std/convert/enum.Infallible.html).
  * A `SEVERITY` constant which is either `Severity::Error` or `Severity::Warning`. Use `Error` if the code is positively impossible to be correct.
  * A `LINT_TYPE` constant which is either `Complexity`, `Correctness`, `Performance`, or `Style`. So far not used for anything.
  * A `new` function with the signature `fn new(config: Self::Config) -> Result<Self, Self::Error>`. With the selene CLI, this is called once.
  * A `pass` function with the signature `fn pass(&self, ast: &full_moon::ast::Ast, context: &Context, ast_context: &AstContext) -> Vec<Diagnostic>`. The `ast` argument is the full-moon representation of the code. The `context` argument provides optional additional information, such as the standard library being used. The `ast_context` argument provides context specific to that AST, such as its scopes. Any `Diagnostic` structs returned here are displayed to the user.

For our purposes, we're going to write:

    use super::*;
    use std::convert::Infallible;

    struct CoolLint;

    impl Lint for CoolLint {
        type Config = ();
        type Error = Infallible;

        const SEVERITY: Severity = Severity::Warning;
        const LINT_TYPE: LintType = LintType::Style;

        fn new(_: Self::Config) -> Result<Self, Self::Error> {
            Ok(CoolLint)

        fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
            unimplemented!()

The implementation of `pass` is completely up to you, but there are a few common patterns.

  * Creating a visitor over the ast provided and creating diagnostics based off of that. See [`divide_by_zero`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/divide_by_zero.rs) and [`suspicious_reverse_loop`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/suspicious_reverse_loop.rs) for straight forward examples.
  * Using the `ScopeManager` struct to lint based off of usage of variables and references. See [`shadowing`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/shadowing.rs) and [`global_usage`](https://github.com/Kampfkarren/selene/blob/master/selene-lib/src/lints/global_usage.rs).

### Getting selene to recognize the new lint

Now that we have our lint, we have to make sure selene actually knows to use it. There are two places you need to update.

In selene-lib/src/lib.rs, search for `use_lints!`. You will see something such as:

    use_lints! {
        almost_swapped: lints::almost_swapped::AlmostSwappedLint,
        divide_by_zero: lints::divide_by_zero::DivideByZeroLint,
        empty_if: lints::empty_if::EmptyIfLint,
        ...

Put your lint in this list (alphabetical order) as the following format:

    lint_name: lints::module_name::LintObject,

For us, this would be:

    cool_lint: lints::cool_lint::CoolLint,

Next, in `selene-lib/src/lints.rs`, search for `pub mod`, and you will see:

    pub mod almost_swapped;
    pub mod divide_by_zero;
    pub mod empty_if;
    ...

Put your module name in this list, also in alphabetical order.

    pub mod almost_swapped;
    pub mod cool_lint;
    pub mod divide_by_zero;
    pub mod empty_if;
    ...

And we're done! You should be able to `cargo build --bin selene` and be able to use your new lint.

### Writing tests

The selene codebase uses tests extensively for lints. It means we never have to actually build the CLI tool in order to test, and we can make sure we don't have any regressions. **Testing is required if you want to submit your lint to the selene codebase.**

To write a simple test, create a folder in `selene-lib/tests` with the name of your lint. Then, create as many `.lua` files as you want to test. These should contain both cases that do and do not lint. For our cases, we're going to assume our test is called `cool_lint.lua`.

Then, in your lint module, add at the bottom:

    #[cfg(test)]
    mod tests {
        use super::{super::test_util::test_lint, *};

        #[test]
        fn test_cool_lint() {
            test_lint(
                CoolLint::new(()).unwrap(),
                "cool_lint",
                "cool_lint",
            );

Let's discuss what this code means, assuming you're familiar with [the way tests are written and performed in Rust](https://doc.rust-lang.org/book/ch11-00-testing.html).

The `test_lint` function is the easiest way to test that a lint works. It'll search the source code we made before, run selene on it (only your lint), and check its results with the existing `[filename].stderr` file, or create one if it does not yet exist.

The first argument is the lint object to use. `CoolLint::new(())` just means "create `CoolLint` with a configuration of `()`". If your lint specifies a configuration, this will instead be something such as `CoolLintConfig::default()` or whatever you're specifically testing.

The `.unwrap()` is just because `CoolLint::new` returns a `Result`. If you want to test configuration errors, you can avoid `test_lint` altogether and just test `CoolLint::new(...).is_err()` directly.

The first `"cool_lint"` is the name of the folder we created. The second `"cool_lint"` is the name of the _Lua file_ we created.

Now, just run `cargo test`, and a `.stderr` file will be automatically generated! You can manipulate it however you see fit as well as modifying your lint, and so long as the file is there, selene will make sure that its accurate.

Optionally, you can add a `.std.toml` with the same name as the test next to the lua file, where you can specify a custom [standard library](https://kampfkarren.github.io/selene/usage/std.html) to use. If you do not, the Lua 5.1 standard library will be used.

### Documenting it

This step is only if you are contributing to the selene codebase, and not just writing personal lints (though I'm sure your other programmers would love if you did this).

To document a new lint, edit `docs/src/SUMMARY.md`, and add your lint to the table of contents along the rest. As with everything else, make sure it's in alphabetical order.

Then, edit the markdown file it creates (if you have `mdbook serve` on, it'll create it for you), and write it in this format:

    # lint_name
    ## What it does
    Explain what your lint does, simply.

    ## Why this is bad
    Explain why a user would want to lint this.

    ## Configuration
    Specify any configuration if it exists.

    ## Example
    ```lua
    -- Bad code here

    ...should be written as...

    ```lua
    -- Good code here

    ## Remarks
    If there's anything else a user should know when using this lint, write it here.

This isn't a strict format, and you can mess with it as appropriate. For example, `standard_library` does not have a "Why this is bad" section as not only is it a very encompassing lint, but it should be fairly obvious. Many lints don't specify a "...should be written as..." as it is either something with various potential fixes (such as [`global_usage`](https://kampfkarren.github.io/selene/lints/global_usage.html)) or because the "good code" is just removing parts entirely (such as [`unbalanced_assignments`](https://kampfkarren.github.io/selene/lints/unbalanced_assignments.html)).

# Lints

The following is the list of lints that selene will check for in your code.

# almost_swapped

## What it does

Checks for `foo = bar; bar = foo` sequences.

## Why this is bad

This looks like a failed attempt to swap.

## Example

    a = b
    b = a

...should be written as...

    a, b = b, a

# constant_table_comparison

## What it does

Checks for direct comparisons with constant tables.

## Why this is bad

This will always fail.

## Example

    if x == { "a", "b", "c" } then

...will never pass.

    if x == {} then

...should be written as...

    if next(x) == nil then

# deprecated

## What it does

Checks for use of deprecated fields and functions, as configured [by your standard library](https://kampfkarren.github.io/selene/usage/std.html#deprecated).

## Why this is bad

Deprecated fields may not be getting any support, or even face the possibility of being removed.

## Configuration

`allow` \- A list of patterns where the deprecated lint will not throw. For instance, `["table.getn"]` will allow you to use `table.getn`, even though it is deprecated. This supports wildcards, so `table.*` will allow both `table.getn` and `table.foreach`.

## Example

    local count = table.getn(x)

...should be written as...

    local count = #x

# divide_by_zero

## What it does

Checks for division by zero. Allows `0 / 0` as a way to get [nan](https://en.wikipedia.org/wiki/NaN).

## Why this is bad

`n / 0` equals `math.huge` when n is positive, and `-math.huge` when n is negative. Use these values directly instead, as using the `/ 0` way is confusing to read and non-idiomatic.

## Example

    print(1 / 0)
    print(-1 / 0)

...should be written as...

    print(math.huge)
    print(-math.huge)

# duplicate_keys

## What it does

Checks for duplicate keys being defined inside of tables.

## Why this is bad

Tables with a key defined more than once will only use one of the values.

## Example

    local foo = {
        a = 1,
        b = 5,
        ["a"] = 3, -- duplicate definition
        c = 3,
        b = 1, -- duplicate definition

    local bar = {
        "foo",
        "bar",
        [1524] = "hello",
        "baz",
        "foobar",
        [2] = "goodbye", -- duplicate to `bar` which has key `2`

## Remarks

Only handles keys which constant string/number literals or named (such as `{ a = true }`). Array-like values are also handled, where `{"foo"}` is implicitly handled as `{ [1] = "foo" }`.

# empty_if

## What it does

Checks for empty if blocks.

## Why this is bad

You most likely forgot to write code in there or commented it out without commenting out the if statement itself.

## Configuration

`comments_count` (default: `false`) - A bool that determines whether or not if statements with exclusively comments are empty.

## Example

    -- Each of these branches count as an empty if.
    if a then
    elseif b then
    else
    end

    if a then
        -- If comments_count is true, this will not count as empty.
    end

# empty_loop

## What it does

Checks for empty loop blocks.

## Why this is bad

You most likely forgot to write code in there or commented it out without commenting out the loop statement itself.

## Configuration

`comments_count` (default: `false`) - A bool that determines whether or not if statements with exclusively comments are empty.

## Example

    -- Counts as an empty loop
    for _ in {} do
    end

    for _ in {} do
        -- If comments_count is true, this will not count as empty.
    end

# global_usage

## What it does

Prohibits use of `_G`.

## Why this is bad

`_G` is global mutable state, which is heavily regarded as harmful. You should instead refactor your code to be more modular in nature.

## Configuration

`ignore_pattern` \- A [regular expression](https://en.wikipedia.org/wiki/Regular_expression) for variables that are allowed to be global variables. The default disallows all global variables regardless of their name.

## Remarks

If you are using the Roblox standard library, use of `shared` is prohibited under this lint.

## Example

    _G.foo = 1

# high_cyclomatic_complexity

## What it does

Measures the [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) of a function to see if it exceeds the configure maximum.

## Why this is bad

High branch complexity can lead to functions that are hard to test, and harder to reason about.

## Configuration

`maximum_complexity` (default: `40`) - A number that determines the maximum threshold for cyclomatic complexity, beyond which the lint will report.

## Example

    function MyComponent(props)
        if props.option1 == "enum_value1" then          -- 1st path
            return React.createElement("Instance")
        elseif props.option1 == "enum_value2"           -- 2nd path
          or props.option2 == nil then                  -- 3rd path
            return React.createElement(
              "TextLabel",
              { Text = if _G.__DEV__ then "X" else "Y" }-- 4th path
            )
        else
            return if props.option2 == true             -- 5th path
              then React.createElement("Frame")
              else nil
        end
    end

## Remarks

This lint is off by default. In order to enable it, add this to your selene.toml:

    [lints]
    high_cyclomatic_complexity = "warn" # Or "deny"

# if_same_then_else

## What it does

Checks for branches in if blocks that are equivalent.

## Why this is bad

This is most likely a copy and paste error.

## Example

    if foo then
        print(1)
    else
        print(1)
    end

# ifs_same_cond

## What it does

Checks for branches in if blocks with equivalent conditions.

## Why this is bad

This is most likely a copy and paste error.

## Example

    if foo then
        print(1)
    elseif foo then
        print(1)
    end

## Remarks

This ignores conditions that could have side effects, such as function calls. This will not lint:

    if foo() then
        print(1)
    elseif foo() then
        print(1)
    end

...as the result of `foo()` could be different the second time it is called.

# incorrect_standard_library_use

## What it does

Checks for correct use of [the standard library](https://kampfkarren.github.io/selene/usage/std.html).

## Example

    for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do

## Remarks

**It is highly recommended that you do not turn this lint off.** If you are having standard library issues, modify your standard library instead to be correct. If it is a problem with an official standard library (Ex: the Lua 5.1 or Roblox ones), you can file an [issue on GitHub](https://github.com/Kampfkarren/selene/issues).

# manual_table_clone

## What it does

Detects manual re-implementations of `table.clone` when it exists in the standard library.

## Why this is bad

`table.clone` is much simpler to read and faster than manual re-implementations.

## Example

    local output = {}

    for key, value in pairs(input) do
        output[key] = value
    end

...should be written as...

    local output = table.clone(input)

## Remarks

Very little outside this exact pattern is matched. This is the list of circumstances which will stop the lint from triggering:

  * Any logic in the body of the function aside from `output[key] = value`.
  * Any usage of the output variable in between the definition and the loop (as determined by position in code).
  * If the input variable is not a plain locally initialized variable. For example, `self.state[key] = value` will not lint.
  * If the input variable is not defined as a completely empty table.
  * If the loop and input variable are defined at different depths.

The detected looping patterns are `pairs(t)`, `ipairs(t)`, `next, t`, and `t` (Luau generalized iteration). If `ipairs` is used, `table.clone` is not an exact match if the table is not exclusively an array. For example:

    local mixedTable = { 1, 2, 3 }
    mixedTable.key = "value"

    local clone = {}

    -- Lints, but is not equivalent, since ipairs only loops over the array part.
    for key, value in ipairs(mixedTable) do
        clone[key] = value
    end

When `ipairs` is the function being used, you'll be notified of this potential gotcha.

# mismatched_arg_count

## What it does

Checks for too many arguments passed to function calls of defined functions.

## Why this is bad

These arguments provided are unnecessary, and can indicate that the function definition is not what was expected.

## Example

    local function foo(a, b)
    end

    foo(1, 2, 3) -- error, function takes 2 arguments, but 3 were supplied

## Remarks

This lint does not handle too few arguments being passed, as this is commonly inferred as passing `nil`. For example, `foo(1)` could be used when meaning `foo(1, nil)`.

If a defined function is reassigned anywhere in the program, it will try to match the best possible overlap. Take this example:

    local function foo(a, b, c)
        print("a")
    end

    function updateFoo()
        foo = function(a, b, c, d)
            print("b")
        end
    end

    foo(1, 2, 3, 4) --> "a" [mismatched args, but selene doesn't know]
    updateFoo()
    foo(1, 2, 3, 4) --> "b" [no more mismatched args]

selene can not tell that `foo` corresponds to a new definition because `updateFoo()` was called in the current context, without actually _running_ the program.

However, this would still lint properly:

    local log

    if SOME_DEBUG_FLAG then
        log = function() end
    else
        log = function(message)
            print(message)
        end
    end

    -- No definition of `log` takes more than 1 argument, so this will lint.
    log("LOG MESSAGE", "Something happened!")

# mixed_table

## What it does

Checks for mixed tables (tables that act as both an array and dictionary).

## Why this is bad

Mixed tables harms readability and are prone to bugs. There is almost always a better alternative.

## Example

    local foo = {
        "array field",
        bar = "dictionary field",

# multiple_statements

## What it does

Checks for multiple statements on the same line.

## Why this is bad

This can make your code difficult to read.

## Configuration

`one_line_if` (default: `"break-return-only"`) - Defines whether or not one line if statements should be allowed. One of three options:

  * "break-return-only" (default) - `if x then return end` or `if x then break end` is ok, but `if x then call() end` is not.
  * "allow" - All one line if statements are allowed.
  * "deny" - No one line if statements are allowed.

## Example

    foo() bar() baz()

...should be written as...

    foo()
    bar()
    baz()

# must_use

## What it does

Checks that the return values of functions [marked `must_use`](https://kampfkarren.github.io/selene/usage/std.html#must_use) are used.

## Why this is bad

This lint will only catch uses where the function has no reason to be called other than to use its output.

## Example

    bit32.bor(entity.flags, Flags.Invincible)

...should be written as...

    entity.flags = bit32.bor(entity.flags, Flags.Invincible)

...as `bit32.bor` only produces a new value, it does not mutate anything.

## Remarks

The output is deemed "unused" if the function call is its own statement.

# parenthese_conditions

## What it does

Checks for conditions in the form of `(expression)`.

## Why this is bad

Lua does not require these, and they are not idiomatic.

## Example

    if (x) then
    repeat until (x)
    while (x) do

...should be written as...

    if x then
    repeat until x
    while x do

# restricted_module_paths

## What it does

Checks for restricted module paths in any expression context, preventing usage of specific module paths wherever they appear in code.

## Why this is bad

Some module paths may be deprecated, internal-only, or have better alternatives that should be used instead. This lint helps enforce coding standards and prevents usage of restricted APIs.

## Configuration

`restricted_paths` \- A map of restricted module paths to their respective error messages.

    [config.restricted_module_paths.restricted_paths]
    "OldLibrary.Utils.deprecatedFunction" = "OldLibrary.Utils.deprecatedFunction has been deprecated. Use NewLibrary.Utils.modernFunction instead."

## Example

    local deprecatedFunction = OldLibrary.Utils.deprecatedFunction

    OldLibrary.Utils.deprecatedFunction()

    fn(OldLibrary.Utils.deprecatedFunction)

    local config = { callback = OldLibrary.Utils.deprecatedFunction }

    function getHandler()
        return OldLibrary.Utils.deprecatedFunction
    end

    local nested = { deep = { handler = OldLibrary.Utils.deprecatedFunction } }

    local handler = condition and OldLibrary.Utils.deprecatedFunction or nil

    local required = require(OldLibrary.Utils.deprecatedFunction)

    global = OldLibrary.Utils.deprecatedFunction

## Remarks

The lint detects restricted module paths in various expression contexts:

  * **Assignments**: `local deprecatedFunction = OldLibrary.Utils.deprecatedFunction`
  * **Function calls**: `OldLibrary.Utils.deprecatedFunction()`
  * **Function arguments**: `fn(OldLibrary.Utils.deprecatedFunction)`
  * **Table constructors**: `local config = { callback = OldLibrary.Utils.deprecatedFunction }`
  * **Return statements**: `return OldLibrary.Utils.deprecatedFunction`
  * **Nested table structures**: `local nested = { deep = { handler = OldLibrary.Utils.deprecatedFunction } }`
  * **Conditional expressions**: `local handler = condition and OldLibrary.Utils.deprecatedFunction or nil`
  * **Require statements**: `local required = require(OldLibrary.Utils.deprecatedFunction)`
  * **Global assignments**: `global = OldLibrary.Utils.deprecatedFunction`

It does not check:

  * **String require statements**: `require("Module.SubModule")`
  * **String literals**: `"Module.SubModule.function"`

The lint performs exact string matching on the full module path, so `"OldLibrary.Utils.deprecatedFunction"` will match exactly but not `"OldLibrary.Utils.deprecatedFunctionExtended"`.

# roblox_incorrect_color3_new_bounds

## What it does

Checks for uses of `Color3.new` where the arguments are not between 0 and 1.

## Why this is bad

Most likely, you are trying to use values of 0 to 255. This will not give you an error, and will silently give you the wrong color. You probably meant to use `Color3.fromRGB` instead.

## Example

    Color3.new(255, 0, 0)

## Remarks

This lint is only active if you are using the Roblox standard library.

# roblox_incorrect_roact_usage

## What it does

Checks for valid uses of createElement. Verifies that class name given is valid and that the properties passed for it are valid for that class.

## Why this is bad

This is guaranteed to fail once it is rendered. Furthermore, the createElement itself will not error--only once it's mounted will it error.

## Example

    -- Using Roact17
    React.createElement("Frame", {
        key = "Valid property for React",
    })

    -- Using legacy Roact
    Roact.createElement("Frame", {
        key = "Invalid property for Roact",
        ThisPropertyDoesntExist = true,
        Name = "This property should not be passed in",

        [Roact.Event.ThisEventDoesntExist] = function() end,
    })

    Roact.createElement("BadClass", {})

## Remarks

This lint is naive and makes several assumptions about the way you write your code. The assumptions are based on idiomatic Roact.

  1. It assumes you are either calling `createElement` directly or creating a local variable that's assigned to `[Roact/React].createElement`.
  2. It assumes if you are using a local variable, you're not reassigning it.
  3. It assumes either Roact or React is defined. [`undefined_variable`](https://kampfkarren.github.io/selene/lints/undefined_variable.html) will still lint, however.

This lint assumes legacy Roact if the variable name is `Roact` and Roact17 if the variable name is named `React`.

This lint does not verify if the value you are giving is correct, so `Text = UDim2.new()` will be treated as correct. This lint, right now, only checks property and class names.

This lint is only active if you are using the Roblox standard library.

# roblox_manual_fromscale_or_fromoffset

## What it does

Checks for uses of `UDim2.new` where the arguments could be simplified to `UDim2.fromScale` or `UDim2.fromOffset`.

## Why this is bad

This reduces readability of `UDim2.new()` construction.

## Example

    UDim2.new(1, 0, 1, 0)

## Remarks

This lint is only active if you are using the Roblox standard library.

# roblox_suspicious_udim2_new

## What it does

Checks for too little arguments passed to `UDim2.new()`.

## Why this is bad

Passing in an incorrect number of arguments can indicate that the user meant to use `UDim2.fromScale` or `UDim2.fromOffset`. Even if the user really only needed to pass in a fewer number of arguments to `UDim2.new`, this lowers readability as it calls into question whether it's a bug or if the user truly meant to use `UDim2.new`.

## Example

    UDim2.new(1, 1) -- error, UDim2.new takes 4 numbers, but 2 were provided.

## Remarks

This lint is only active if you are using the Roblox standard library.

This lint does not warn if passing in exactly 2 arguments and none of those are number literals to prevent false positives with `UDim2.new(UDim.new(a, b), UDim.new(c, d))`

# shadowing

## What it does

Checks for overriding of variables under the same name.

## Why this is bad

This can cause confusion when reading the code when trying to understand which variable is being used, and if you want to use the original variable you either have to redefine it under a temporary name or refactor the code that shadowed it.

## Configuration

`ignore_pattern` (default: `"^_"`) - A [regular expression](https://en.wikipedia.org/wiki/Regular_expression) that is used to specify names that are allowed to be shadowed. The default allows for variables like `_` to be shadowed, as they shouldn't be used anyway.

## Example

    local x = 1

    if foo then
        local x = 1
    end

# suspicious_reverse_loop

## What it does

Checks for `for _ = #x, 1 do` sequences without specifying a negative step.

## Why this is bad

This loop will only run at most once, instead of going in reverse. If you truly did mean to run your loop only once, just use `if #x > 0` instead.

## Example

    for _ = #x, 1 do

...should be written as...

    for _ = #x, 1, -1 do

# type_check_inside_call

## What it does

Checks for `type(foo == "type")`, instead of `type(foo) == "type"`.

## Why this is bad

This will always return `"boolean"`, and is undoubtedly not what you intended to write.

## Example

    return type(foo == "number")

...should be written as...

    return type(foo) == "number"

## Remarks

When using the Roblox standard library, this checks `typeof` as well.

# unbalanced_assignments

## What it does

Checks for unbalanced assignments, such as `a, b, c = 1`.

## Why this is bad

You shouldn't declare variables you're not immediately initializing on the same line as ones you are. This is most likely just forgetting to specify the rest of the variables.

## Example

    a, b, c = 1
    a = 1, 2

## Remarks

There are a few things this lint won't catch.

`a, b, c = call()` will not lint, as `call()` could return multiple values.

`a, b, c = call(), 2` will, however, as you will only be using the first value of `call()`. You will even receive a helpful message about this.

    error[unbalanced_assignments]: values on right side don't match up to the left side of the assignment

       ┌── unbalanced_assignments.lua:6:11 ───
       │
     6 │ a, b, c = call(), 2
       │           ^^^^^^^^^
       │

       ┌── unbalanced_assignments.lua:6:11 ───
       │
     6 │ a, b, c = call(), 2
       │           ------ help: if this function returns more than one value, the only first return value is actually used
       │

If nil is specified as the last value, the rest will be ignored. This means...

    a, b, c = nil

...will not lint.

# undefined_variable

## What it does

Checks for uses of variables that are not defined.

## Why this is bad

This is most likely a typo.

## Example

    -- vv oops!
    prinnt("hello, world!")

## Remarks

If you are using a different standard library where a global variable is defined that selene isn't picking up on, create a [standard library](https://kampfkarren.github.io/selene/usage/std.html) that specifies it.

# unscoped_variables

## What it does

Checks for variables that are unscoped (don't have a local variable attached).

## Why this is bad

Unscoped variables make code harder to read and debug, as well as making it harder for selene to analyze.

## Configuration

`ignore_pattern` (default: `"^_"`) - A [regular expression](https://en.wikipedia.org/wiki/Regular_expression) for variables that are allowed to be unscoped. The default allows for variables like `_` to be unscoped, as they shouldn't be used anyway.

## Example

    baz = 3

# unused_variable

## What it does

Checks for variables that are unused.

## Why this is bad

The existence of unused variables could indicate buggy code.

## Configuration

`allow_unused_self` (default: `true`) - A bool that determines whether not using `self` in a method function (`function Player:SwapWeapons()`) is allowed.

`ignore_pattern` (default: `"^_"`) - A [regular expression](https://en.wikipedia.org/wiki/Regular_expression) for variables that are allowed to be unused. The default allows for variables like `_` to be unused, as they shouldn't be used anyway.

## Example

    local foo = 1

## Remarks

### `_` prefixing

If you intend to create a variable without using it, replace it with `_` or something that starts with `_`. You'll see this most in generic for loops.

    for _, value in ipairs(list) do

### `observes`

Standard libraries can apply [an `observes` field](https://kampfkarren.github.io/selene/usage/std.html#observes) to distinguish an argument from being only written to.

This is so that we can get lints like the following:

    local writtenOnly = {}
    table.insert(writtenOnly, 1)

    warning[unused_variable]: writtenOnly is assigned a value, but never used
      ┌─ example.lua:1:7
      │
    1 │ local writtenOnly = {}
      │       ^^^^^^^^^^^
    2 │ table.insert(writtenOnly, 1)
      │              ----------- `table.insert` only writes to `writtenOnly`

This only applies when the function call is its own statement. So for instance, this:

    local list = {}
    print(table.insert(list, 1))

...will _not_ lint. To understand this, consider if `table.insert` returned the index. Without this check, this code:

    local list = {}
    local index = table.insert(list, 1)

...would lint `list` as mutated only, which while technically true, is unimportant considering `index` is affected by the mutation.

This also requires that the variable be a static table. This:

    return function(value)
        table.insert(value, 1)
    end

...will not lint, as we cannot be sure `value` is unused outside of this.

# Archive

The following is pages that refer to deprecated or removed features in selene.

# Standard Library Format (v1)

> The TOML standard library format is **DEPRECATED** and will not have any new functionality added onto it. Check out the [updated standard library format here](https://kampfkarren.github.io/selene/usage/std.html).
> In order to convert an existing TOML standard library over to the new format, simply run `selene upgrade-std library.toml`, which will create an upgraded `library.yml` file.

selene provides a robust standard library format to allow for use with environments other than vanilla Lua. Standard libraries are defined in the form of [TOML](https://github.com/toml-lang/toml) files.

## Examples

For examples of the standard library format, see:

  * [`lua51.toml`](https://github.com/Kampfkarren/selene/blob/416be350d7004bf300a83f402712f8df6a3f67da/selene-lib/default_std/lua51.toml) \- The default standard library for Lua 5.1
  * [`lua52.toml`](https://github.com/Kampfkarren/selene/blob/416be350d7004bf300a83f402712f8df6a3f67da/selene-lib/default_std/lua52.toml) \- A standard library for Lua 5.2's additions and removals. Reference this if your standard library is based off another (it most likely is).
  * [`roblox.toml`](https://gist.github.com/evaera/13c96302d308c7a9ffb2a3fc5d28ac96) \- A standard library for Roblox that incorporates all the advanced features of the format. If you are a Roblox developer, don't use this as anything other than reference--an up to date version of this library is available with every commit.

## [selene]

Anything under the key `[selene]` is used for meta information. The following paths are accepted:

`[selene.base]` \- Used for specifying what standard library to be based off of. Currently only accepts built in standard libraries, meaning `lua51` or `lua52`.

`[selene.name]` \- Used for specifying the name of the standard library. Used internally for cases such as only giving Roblox lints if the standard library is named `"roblox"`.

`[selene.structs]` \- Used for declaring [structs](https://kampfkarren.github.io/selene/archive/std_v1.html#structs).

## [globals]

This is where the magic happens. The `globals` field is a dictionary where the keys are the globals you want to define. The value you give tells selene what the value can be, do, and provide.

If your standard library is based off another, overriding something defined there will use your implementation over the original.

## Any

Example:

    [foo]
    any = true

Specifies that the field can be used in any possible way, meaning that `foo.x`, `foo:y()`, etc will all validate.

## Functions

Example:

    [[tonumber.args]]
    type = "any"

    [[tonumber.args]]
    type = "number"
    required = false

A field is a function if it contains an `args` and/or `method` field.

If `method` is specified as `true` and the function is inside a table, then it will require the function be called in the form of `Table:FunctionName()`, instead of `Table.FunctionName()`.

`args` is an array of arguments, in order of how they're used in the function. An argument is in the form of:

    required?: false | true | string;
    type: "any" | "bool" | "function" | "nil"
        | "number" | "string" | "table" | "..."
        | string[] | { "display": string }

## "required"

  * `true` \- The default, this argument is required.
  * `false` \- This argument is optional.
  * A string - This argument is required, and not using it will give this as the reason why.

## Argument types

  * `"any"` \- Allows any value.
  * `"bool"`, `"function"`, `"nil"`, `"number"`, `"string"`, `"table"` \- Expects a value of the respective type.
  * `"..."` \- Allows any number of variables after this one. If `required` is true (it is by default), then this will lint if no additional arguments are given. It is incorrect to have this in the middle.
  * Constant list of strings - Will check if the value provided is one of the strings in the list. For example, `collectgarbage` only takes one of a few exact string arguments--doing `collectgarbage("count")` will work, but `collectgarbage("whoops")` won't.
  * `{ "display": string }` \- Used when no constant could possibly be correct. If a constant is used, selene will tell the user that an argument of the type (display) is required. For an example, the Roblox method `Color3.toHSV` expects a `Color3` object--no constant inside it could be correct, so this is defined as:

    [[Color3.toHSV.args]]
    type = { display = "Color3" }

## Properties

Example:

    [_VERSION]
    property = true

Specifies that a property exists. For example, `_VERSION` is available as a global and doesn't have any fields of its own, so it is just defined as a property.

The same goes for `_G`, which is defined as:

    [_G]
    property = true
    writable = "new-fields"

`writable` is an optional field that tells selene how the property can be mutated and used:

  * `"new-fields"` \- New fields can be added and set, but variable itself cannot be redefined. In the case of _G, it means that `_G = "foo"` is linted against.
  * `"overridden"` \- New fields can't be added, but entire variable can be overridden. In the case of Roblox's `Instance.Name`, it means we can do `Instance.Name = "Hello"`, but not `Instance.Name.Call()`.
  * `"full"` \- New fields can be added and entire variable can be overridden.

If `writable` is not specified, selene will assume it can neither have new fields associated with it nor can be overridden.

## Struct

Example:

    [game]
    struct = "DataModel"

Specifies that the field is an instance of a [struct](https://kampfkarren.github.io/selene/archive/std_v1.html#structs). The value is the name of the struct.

## Table

Example:

    [math.huge]
    property = true

    [math.pi]
    property = true

A field is understood as a table if it has fields of its own. Notice that `[math]` is not defined anywhere, but its fields are. Fields are of the same type as globals.

## Removed

Example:

    [getfenv]
    removed = true

Used when your standard library is based off another, and your library removes something from the original.

## Structs

Structs are used in places such as Roblox Instances. Every Instance in Roblox, for example, declares a `:GetChildren()` method. We don't want to have to define this everywhere an Instance is declared globally, so instead we just define it once in a struct.

Structs are defined as fields of `[selene.structs]`. Any fields they have will be used for instances of that struct. For example, the Roblox standard library has the struct:

    [selene.structs.Event.Connect]
    method = true

    [[selene.structs.Event.Connect.args]]
    type = "function"

From there, it can define:

    [workspace.Changed]
    struct = "Event"

...and selene will know that `workspace.Changed:Connect(callback)` is valid, but `workspace.Changed:RandomNameHere()` is not.

## Wildcards

Fields can specify requirements if a field is referenced that is not explicitly named. For example, in Roblox, instances can have arbitrary fields of other instances (`workspace.Baseplate` indexes an instance named Baseplate inside `workspace`, but `Baseplate` is nowhere in the Roblox API).

We can specify this behavior by using the special `"*"` field.

    [workspace."*"]
    struct = "Instance"

This will tell selene "any field accessed from `workspace` that doesn't exist must be an Instance [struct](https://kampfkarren.github.io/selene/archive/std_v1.html#structs)".

Wildcards can even be used in succession. For example, consider the following:

    [script.Name]
    property = true
    writable = "overridden"

    [script."*"."*"]
    property = true
    writable = "full"

Ignoring the wildcard, so far this means:

  * `script.Name = "Hello"` _will_ work.
  * `script = nil` _will not_ work, because the writability of `script` is not specified.
  * `script.Name.UhOh` _will not_ work, because `script.Name` does not have fields.

However, with the wildcard, this adds extra meaning:

  * `script.Foo = 3` _will not_ work, because the writability of `script.*` is not specified.
  * `script.Foo.Bar = 3` _will_ work, because `script.*.*` has full writability.
  * `script.Foo.Bar.Baz = 3` _will_ work for the same reason as above.


# Luacheck Comparison

## selene vs. luacheck

selene is not the first Lua linter. The main inspiration behind selene is [luacheck](https://luacheck.readthedocs.io/en/stable/). However, the two have very little in common besides inception.

  * selene is actively maintained, while at the time of writing luacheck's last commit was in October 2018.
  * selene is written in Rust, while luacheck is written in Lua. In practice, this means that selene is much faster than luacheck while also being able to easily take advantage of features luacheck cannot because of the difficulty of using dependencies in Lua.
  * selene is multithreaded, again leading to significantly better performance.
  * selene has rich output, while luacheck has basic output.

selene:

    error[suspicious_reverse_loop]: this loop will only ever run once at most

       ┌── fail.lua:1:9 ───
       │
     1 │ for _ = #x, 1 do
       │         ^^^^^
       │
       = help: try adding `, -1` after `1`

luacheck:

    Checking fail.lua                                 2 warnings

        fail.lua:1:1: numeric for loop goes from #(expr) down to 1 but loop step is not negative

  * selene uses [TOML](https://github.com/toml-lang/toml) files for configuration, while luacheck uses `.luacheckrc`, which runs Lua.
  * selene allows for [standard library configuration](https://kampfkarren.github.io/selene/usage/std.html) such as argument types, argument counts, etc, while luacheck only allows knowing that fields exist and can be written to. In practice, this means that selene catches:

    for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do

    math.pi()

...while luacheck does not.

  * selene has English names for lints instead of arbitrary numbers. In luacheck, you ignore "[`211`](https://luacheck.readthedocs.io/en/stable/warnings.html#unbalanced-assignments)", while in selene you ignore "[`unbalanced_assignments`](https://kampfkarren.github.io/selene/lints/unbalanced_assignments.html)".
  * selene has distinctions for "deny" and "warn", while every luacheck lint is the same.
  * selene has a much simpler codebase, and is much easier to add your own lints to.
  * selene has optional support and large focus specifically for Roblox development.
  * selene will only show you files that lint, luacheck only does this with the `-q` option (quiet).
  * selene [filters specific lints](https://kampfkarren.github.io/selene/usage/filtering.html) and applies over code rather than lines, luacheck does not.
  * selene has [significantly more lints](https://kampfkarren.github.io/selene/lints/index.html).

This is not to say selene is objectively better than luacheck, at least not yet.

  * luacheck has lints for long lines and whitespace issues, selene does not as it is unclear whether style issues like these are fit for a linter or better under the scope of a Lua beautifier.
  * luacheck officially supports versions past Lua 5.1, selene does not yet as there is not much demand.
  * luacheck supports the following lints that selene does not yet:
    * Unreachable code
    * Unused labels (selene does not officially support Lua 5.2 yet)
    * Detecting variables that are only ever mutated, but not read
    * Using uninitialized variables

## Migration

luacheck does not require much configuration to begin with, so migration should be easy.

  * You can configure what lints are allowed in the [configuration](https://kampfkarren.github.io/selene/usage/configuration.html#changing-the-severity-of-lints).
  * Do you have a custom standard library (custom globals, functions, etc)? Read the [standard library guide](https://kampfkarren.github.io/selene/usage/std.html).
    * Are you a Roblox developer using something like [luacheck-roblox](https://github.com/Quenty/luacheck-roblox/)? A featureful standard library for Roblox is generated with every commit on GitHub. TODO: Have a flag in the selene CLI to generate a Roblox standard library a la `generate-roblox-std`? Should `generate-roblox-std` be uploaded to crates.io?


# Command Line Interface

**selene** is mostly intended for use as a command line tool.

In this section, you will learn how to use selene in this manner.


# Motivation

## Because bugs

When writing any code, it's very easy to make silly mistakes that end up introducing bugs. A lot of the time, these bugs are hard to track down and debug, and sometimes are even harder to replicate.

This risk is made ever more real because of the generally lax nature of Lua. Incorrect code is regularly passed off and isn't noticed until something breaks at runtime. Sometimes you'll get a clear error message, and will have to spend time going back, fixing the code, and making sure you actually fixed it. Other times, the effects are more hidden, and instead of getting an error your code will just pass through along, in an incorrect state.

Take, for example, this code:

    function Player:SwapWeapons()
        self.CurrentWeapon = self.SideWeapon
        self.SideWeapon = self.CurrentWeapon
    end

This is code that is technically correct, but is absolutely not what you wanted to write. However, because it is _technically_ correct, Lua will do exactly what you tell it to do, and so...

  * Player wants to swap their weapons
  * Your code calls `player:SwapWeapons()`
  * Their current weapon is set to their side weapon...
  * ...but their side weapon is set to their current weapon afterwards, which is what they just equipped!

Uh oh! After debugging this, you realize that you actually meant to write was...

    function Player:SwapWeapons()
        self.CurrentWeapon, self.SideWeapon = self.SideWeapon, self.CurrentWeapon
    end

If you were using selene, you would've been alerted right away that your original code looked like an [`almost_swapped`](https://kampfkarren.github.io/selene/lints/almost_swapped.html).

    error[almost_swapped]: this looks like you are trying to swap `self.CurrentWeapon` and `self.SideWeapon`

       ┌── fail.lua:4:5 ───
       │
     4 │ ╭     self.CurrentWeapon = self.SideWeapon
     5 │ │     self.SideWeapon = self.CurrentWeapon
       │ ╰────────────────────────────────────────^
       │
       = try: `self.CurrentWeapon, self.SideWeapon = self.SideWeapon, self.CurrentWeapon`

Other bugs arise because of Lua's lack of typing. While it can feel freeing to developers to not have to specify types everywhere, it makes it easier to mess up and write broken code. For example, take the following code:

    for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do

This code is yet again technically correct, but not what we wanted to do. `pairs` will take the first argument, `GoldShop`, and ignore the rest. Worse, the `shop` variable will now be the values of the contents of `GoldShop`, not the shop itself. This can cause massive headaches, since although you're likely to get an error later down the line, it's more likely it'll be in the vein of "attempt to index a nil value `items`" than something more helpful. If you used `ipairs` instead of `pairs`, your code inside might just not run, and won't produce an error at all.

Yet again, selene saves us.

    error[incorrect_standard_library_use]: standard library function `pairs` requires 1 parameters, 3 passed

       ┌── fail.lua:1:16 ───
       │
     1 │ for _, shop in pairs(GoldShop, ItemShop, MedicineShop) do
       │                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       │

This clues the developer into writing the code they meant to write:

    for _, shop in pairs({ GoldShop, ItemShop, MedicineShop }) do

### Because idiomatic Lua

While it's nice to write code however you want to, issues can arise when you are working with other people, or plan on open sourcing your work for others to contribute to. It's best for everyone involved if they stuck to the same way of writing Lua.

Consider this contrived example:

    call(1 / 0)

The person who wrote this code might have known that `1 / 0` evaluates to `math.huge`. However, anyone working on that code will likely see it and spend some time figuring out why they wrote the code that way.

If the developer was using selene, this code would be denied:

    warning[divide_by_zero]: dividing by zero is not allowed, use math.huge instead

       ┌── fail.lua:1:6 ───
       │
     1 │ call(1 / 0)
       │      ^^^^^
       │

Furthermore, selene is meant to be easy for developers to add their own lints to. You could create your own lints for your team to prevent behavior that is non-idiomatic to the codebase. For example, let's say you're working on a [Roblox](https://developer.roblox.com/en-us) codebase, and you don't want your developers using the data storage methods directly. You could create your own lint so that this code:

    local DataStoreService = game:GetService("DataStoreService")

...creates a warning, discouraging its use. For more information on how to create your own lints, check out the [contribution guide](https://kampfkarren.github.io/selene/contributing.html).