EmmyLua Analyzer provides a powerful static analysis and error detection system. This page explains how to configure diagnostic rules, adjust severity levels, and control diagnostics at the file and line level.
Diagnostic Settings
Enable/Disable Diagnostics
Master switch to enable or disable the entire diagnostic system. {
"diagnostics" : {
"enable" : false // Disable all diagnostics
}
}
diagnostics.diagnosticInterval
Delay between opening/changing a file and scanning it for errors, in milliseconds. Increase this value if the analyzer is consuming too many resources. {
"diagnostics" : {
"diagnosticInterval" : 1000 // Wait 1 second before analyzing
}
}
Disable Specific Diagnostics
List of diagnostic codes to disable globally across your project. {
"diagnostics" : {
"disable" : [
"undefined-global" ,
"unused-local" ,
"lowercase-global"
]
}
}
Enable Specific Diagnostics
List of diagnostic codes to explicitly enable. Useful when you want to enable diagnostics that are disabled by default. {
"diagnostics" : {
"enables" : [
"undefined-field" ,
"missing-fields"
]
}
}
Severity Configuration
Map diagnostic codes to custom severity levels. This allows you to make warnings into errors, or errors into hints. Severity Levels :
error: Red squiggle, blocks some operations
warning: Yellow squiggle
information: Blue squiggle
hint: Subtle gray squiggle
Strict Project
Lenient Project
{
"diagnostics" : {
"severity" : {
"param-type-mismatch" : "error" ,
"return-type-mismatch" : "error" ,
"assign-type-mismatch" : "error" ,
"unused-local" : "warning"
}
}
}
Global Variables
Whitelist Global Variables
List of global variable names that should not trigger undefined-global warnings. {
"diagnostics" : {
"globals" : [ "vim" , "love" , "ngx" , "jit" , "bit" ]
}
}
Global Variable Patterns
List of regular expression patterns for global variables. Useful for matching multiple globals with similar names. {
"diagnostics" : {
"globalsRegex" : [
"^GLOBAL_.*" , // GLOBAL_CONFIG, GLOBAL_DEBUG, etc.
"^_G \\ ..*" , // _G.anything
"^[A-Z_]+$" // Any uppercase with underscores
]
}
}
Available Diagnostic Codes
Syntax and Parsing
Syntax errors in Lua code. -- Triggers syntax-error
function foo (
-- Missing closing parenthesis
Syntax errors in EmmyLua documentation comments. --- @param invalid syntax here
function foo () end
Type Checking
Referenced type is not defined. --- @type UndefinedType
local var
param-type-mismatch (Warning)
Parameter type doesn’t match the expected type. --- @param name string
function greet ( name )
print ( name )
end
greet ( 123 ) -- param-type-mismatch: expected string, got number
return-type-mismatch (Warning)
Return value type doesn’t match the declared return type. --- @return string
function getName ()
return 123 -- return-type-mismatch: expected string, got number
end
assign-type-mismatch (Warning)
Assignment type doesn’t match the variable’s type. --- @type string
local name = 123 -- assign-type-mismatch
Function Analysis
Function is missing a return statement but declares a return type. --- @return string
function getName ()
-- missing-return: no return statement
end
missing-return-value (Warning)
Return statement is missing required values. --- @return string , number
function getData ()
return "hello" -- missing-return-value: expected 2 values, got 1
end
redundant-return-value (Warning)
Return statement has more values than declared. --- @return string
function getName ()
return "hello" , 123 -- redundant-return-value
end
missing-parameter (Warning)
Function call is missing required parameters. --- @param name string
--- @param age number
function register ( name , age ) end
register ( "Alice" ) -- missing-parameter: age required
redundant-parameter (Warning)
Function call has more parameters than expected. --- @param name string
function greet ( name ) end
greet ( "Alice" , "extra" ) -- redundant-parameter
Variables and Scope
Global variable is used but not defined. print ( undefinedVariable ) -- undefined-global
undefined-field (Warning)
Field access on undefined property. --- @class User
--- @field name string
local user = {}
print ( user . email ) -- undefined-field
Variable or function is defined but never used. local unusedVar = 10 -- unused
Local variable is redefined in the same scope. local x = 1
local x = 2 -- redefined-local
local-const-reassign (Error)
Attempting to reassign a constant local variable. ---@const
local MAX_SIZE = 100
MAX_SIZE = 200 -- local-const-reassign
iter-variable-reassign (Warning)
Iterator variable is being reassigned in the loop body. for i = 1 , 10 do
i = i + 1 -- iter-variable-reassign
end
Classes and Fields
Table literal is missing required fields from its type. --- @class Config
--- @field host string
--- @field port number
--- @type Config
local config = { -- missing-fields: port required
host = "localhost"
}
Field is being injected into a class that doesn’t declare it. --- @class User
--- @field name string
local user = { name = "Alice" }
user . age = 25 -- inject-field
Type is defined multiple times. --- @alias Status string
--- @alias Status string -- duplicate-type
circle-doc-class (Warning)
Circular inheritance in class definitions. --- @class A : B
--- @class B : A -- circle-doc-class
Code Quality
Code that can never be executed. function foo ()
return true
print ( "never runs" ) -- unreachable-code
end
Using a deprecated symbol. --- @deprecated Use newFunction instead
function oldFunction () end
oldFunction () -- deprecated
code-style-check (Warning)
Code doesn’t follow configured style guidelines.
duplicate-set-field (Warning)
Field is assigned multiple times in the same table literal. local config = {
debug = true ,
debug = false -- duplicate-set-field
}
duplicate-index (Warning)
Array index is used multiple times in the same table literal. local arr = {
[ 1 ] = "a" ,
[ 1 ] = "b" -- duplicate-index
}
Module is required multiple times. local json = require ( "json" )
local json2 = require ( "json" ) -- duplicate-require
Advanced
Value might be nil and should be checked before use. --- @param value string ?
function process ( value )
print ( value : upper ()) -- need-check-nil
end
access-invisible (Warning)
Accessing a private or protected member from outside the class. --- @class MyClass
--- @field private _internal string
local obj = MyClass . new ()
print ( obj . _internal ) -- access-invisible
discard-returns (Warning)
Function return values are being discarded when they should be used. --- @return string
--- @nodiscard
function getValue () return "value" end
getValue () -- discard-returns
cast-type-mismatch (Warning)
Type cast is invalid. --- @type string
local str = "hello"
--- @cast str number -- cast-type-mismatch
generic-constraint-mismatch (Warning)
Generic type constraint is violated. --- @generic T : string
--- @param value T
function process ( value ) end
process ( 123 ) -- generic-constraint-mismatch
File-Level Diagnostic Control
Use the ---@diagnostic annotation to control diagnostics at the file, block, or line level.
Syntax
--- @diagnostic < action >: < diagnostic_name >[ , < diagnostic_name >...]
Actions :
disable: Disable diagnostics until re-enabled
disable-next-line: Disable for the next line only
disable-line: Disable for the current line only
enable: Re-enable previously disabled diagnostics
Line-Level Control
disable-next-line
disable-line
Multiple diagnostics
-- Disable specific diagnostic for the next line
--- @diagnostic disable-next-line : undefined-global
print ( someUndefinedVariable )
Block-Level Control
-- Disable for a block of code
do
--- @diagnostic disable : undefined-global
someGlobalVar = "this is OK"
anotherGlobalVar = 42
--- @diagnostic enable : undefined-global
end
-- This line will show warnings again
thirdGlobal = "not OK"
File-Level Control
-- At the top of the file, disable diagnostics for the entire file
--- @diagnostic disable : undefined-global , lowercase-global
-- All code below will have these diagnostics disabled
GLOBAL_CONFIG = {
debug = true
}
function setup ()
ANOTHER_GLOBAL = true
end
Common Configuration Patterns
Strict Type Checking
{
"diagnostics" : {
"severity" : {
"param-type-mismatch" : "error" ,
"return-type-mismatch" : "error" ,
"assign-type-mismatch" : "error" ,
"missing-return" : "error" ,
"need-check-nil" : "error"
}
},
"strict" : {
"typeCall" : true ,
"arrayIndex" : true
}
}
Game Development (Lenient)
{
"diagnostics" : {
"globals" : [ "love" ],
"disable" : [
"lowercase-global" ,
"undefined-field"
],
"severity" : {
"param-type-mismatch" : "hint" ,
"unused-local" : "hint"
}
}
}
Library Development
{
"diagnostics" : {
"disable" : [ "unused-local" ],
"severity" : {
"missing-return" : "error" ,
"param-type-mismatch" : "error" ,
"incomplete-signature-doc" : "warning" ,
"missing-global-doc" : "warning"
}
}
}
Legacy Codebase
{
"diagnostics" : {
"disable" : [
"undefined-global" ,
"lowercase-global" ,
"undefined-field"
],
"severity" : {
"param-type-mismatch" : "hint" ,
"return-type-mismatch" : "hint"
}
}
}
Best Practices
Start with defaults, adjust gradually
Begin with the default diagnostic settings and only disable rules that cause too many false positives in your specific codebase.
Use file-level control for special cases
Instead of disabling diagnostics globally, use ---@diagnostic annotations for specific files that need exceptions (e.g., test files, compatibility layers).
Don’t disable diagnostics unless absolutely necessary. Consider changing severity to hint instead of disabling completely.
Document your configuration
Add comments in your .emmyrc.json explaining why certain diagnostics are disabled or have custom severity.
Use global whitelists sparingly
Prefer declaring globals with ---@type annotations in your code rather than adding them to the global whitelist.