Overview of selene-vscode
mainselene-vscode package provides VS Code integration for Selene, a high-performance Lua linter written in Rust. It allows developers to use Selene's linting capabilities directly within the VS Code editor environment.repository·main·Indexed 21 days ago
https://github.com/kampfkarren/seleneA high-performance, modern Lua linter written in Rust designed for diagnostic accuracy, extensibility, and minimal configuration. It includes a CLI for checking files and directories, a VS Code integration via selene-vscode, and support for Roblox-specific lints.
selene-vscode package provides VS Code integration for Selene, a high-performance Lua linter written in Rust. It allows developers to use Selene's linting capabilities directly within the VS Code editor environment.The incorrect_standard_library_use lint checks whether you are using the standard library correctly according to Selene's definitions. It identifies calls to standard library functions that use incorrect arguments or patterns.
Note: It is highly recommended that you do not disable this lint. If you encounter issues with the standard library, you should aim to correct your usage or modify your standard library definition to be accurate rather than turning the lint off.
-- Example of code that might trigger this lint:
for _, shop in pairs(GoldShop, ItemShop, MedicineShop) doThe restricted_module_paths lint scans expression contexts to find restricted module paths. It is designed to catch usage in various scenarios, including:
local x = Restricted.PathRestricted.Path()fn(Restricted.Path){ key = Restricted.Path }return Restricted.Path{ a = { b = Restricted.Path } }condition and Restricted.Path or nilrequire(Restricted.Path)global = Restricted.PathThe lint does not check:
require("Module.SubModule")"Module.SubModule.function"It also uses exact string matching; for example, a restriction on A.B will not trigger for A.BExtended.
-- Example of what triggers the lint:
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.deprecatedFunctionWhen using ipairs to clone a table, table.clone is not an exact functional match if the table contains non-array keys (mixed tables). ipairs only iterates over the array part of a table, whereas table.clone clones the entire table.
If you use ipairs in a pattern that matches the manual clone lint, you will be notified of this potential discrepancy.
Example of a non-equivalent clone:
local mixedTable = { 1, 2, 3 }
mixedTable.key = "value"
local clone = {}
-- This lints, but is NOT equivalent to table.clone because it misses 'key'
for key, value in ipairs(mixedTable) do
clone[key] = value
endlocal 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
endWildcards (*) allow you to specify requirements for fields that are not explicitly named in your standard library. This is useful for environments where objects can have arbitrary child properties.
workspace.*: Any field accessed from workspace that isn't explicitly defined will be treated as a specific struct.script.*.* allow you to define writability for deeply nested dynamic paths.# Any field accessed from workspace that doesn't exist must be an Instance struct
workspace.*:
struct: Instance
# Deeply nested dynamic fields have full writability
script.*.*:
property: full-writeThe duplicate_keys rule has specific limitations on what it detects:
{ a = true }).{"foo"} is treated as { [1] = "foo" }.If you are considering switching from luacheck to selene, consider these key differences:
selene is written in Rust and is multithreaded, making it significantly faster than the Lua-based luacheck.selene uses TOML files, whereas luacheck uses .luacheckrc (which executes Lua code).selene provides rich, actionable error messages with visual pointers and help suggestions. luacheck provides basic text warnings.selene supports advanced standard library configuration (argument types, counts, etc.), allowing it to catch errors like incorrect function calls (e.g., math.pi()) that luacheck misses.selene uses descriptive English names for lints (e.g., unbalanced_assignments) instead of numeric codes. It also distinguishes between deny and warn severities.selene allows filtering specific lints and applies rules over code blocks rather than just individual lines.selene has optional support and a large focus specifically for Roblox development.What selene does NOT currently do (compared to luacheck):
The global_usage lint is designed to prevent the use of _G, which represents global mutable state. Using _G is considered harmful because it makes code harder to reason about and less modular. Instead of using _G, you should refactor your code to be more modular.
Key constraints:
_G.shared is also prohibited.ignore_pattern (a regex).-- This will trigger the global_usage lint:
_G.foo = 1The shadowing rule detects when a variable name is reused in a nested scope, effectively hiding the original variable. This is flagged because it can cause confusion when reading code and makes it difficult to access the original variable without refactoring or renaming.
local x = 1
if foo then
local x = 1 -- This shadows the outer 'x'
endThe mismatched_arg_count lint identifies instances where more arguments are passed to a function call than are defined in that function's signature. This is used to catch unnecessary arguments that might indicate a misunderstanding of the function's API.
Limitations:
nil (e.g., foo(1) is often used intentionally instead of foo(1, nil)).local function foo(a, b)
end
foo(1, 2, 3) -- error, function takes 2 arguments, but 3 were suppliedThe globals field is a dictionary where keys are the global names. The value defines how Selene validates that global. You can define globals as:
any: true to allow any usage (indexing, calling, etc.).args and/or method.property key to specify writability.struct key to link to a named struct.