Skip to content
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d8c3344
feat(diagnostics): add missing-export-doc minimal implementation
NathanSnail Dec 2, 2025
af9892d
feat(diagnostics): add translations for missing-export-doc messages
NathanSnail Dec 2, 2025
9e4659d
test(diagnostics): add tests for all missing-export-doc cases
NathanSnail Dec 2, 2025
655a97f
test(diagnostics): make missing comment diagnostics have consistent r…
NathanSnail Dec 2, 2025
6743fbd
fix(diagnostics): fix missing-export-doc triggering for missing-local…
NathanSnail Dec 2, 2025
f644444
fix(diagnostics): fix methods never being accepted for missing-export…
NathanSnail Dec 2, 2025
65909aa
fix(diagnostics): fix undocumented method returns never being ignored…
NathanSnail Dec 2, 2025
dd52dcc
fix(diagnostics): fix undocumented method returns never being ignored…
NathanSnail Dec 2, 2025
b24a140
fix(diagnostics): fix bind doc comments not being detected for a.b = …
NathanSnail Dec 2, 2025
6c613e6
test(diagnostics): the function node on a method isn't a direct ances…
NathanSnail Dec 2, 2025
ab2dfbf
fix(diagnostics): fix arg list using the diagnostic node rather than …
NathanSnail Dec 2, 2025
497eb21
fix(diagnostics): fix arg list thinking self was an arg with type spe…
NathanSnail Dec 2, 2025
a46f959
fix(diagnostics): fix unbalanced assignments causing a nil index for …
NathanSnail Dec 2, 2025
e2368ad
feat(diagnostics): write an actual description for missing-export-doc
NathanSnail Dec 2, 2025
03a6197
update changelog.md
NathanSnail Dec 2, 2025
7f1284c
fix ordering of DIAG_* translations
NathanSnail Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions locale/en-us/script.lua
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ DIAG_MISSING_LOCAL_EXPORT_DOC_COMMENT =
'Missing comment for exported local function `{}`.'
DIAG_MISSING_LOCAL_EXPORT_DOC_PARAM =
'Missing @param annotation for parameter `{}` in exported local function `{}`.'
DIAG_MISSING_EXPORTED_METHOD_DOC_RETURN =
'Missing @return annotation at index `{}` in exported method `{}`.'
DIAG_MISSING_EXPORTED_METHOD_DOC_COMMENT =
'Missing comment for exported method `{}`.'
DIAG_MISSING_EXPORTED_METHOD_DOC_PARAM =
'Missing @param annotation for parameter `{}` in exported method `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_RETURN =
'Missing @return annotation at index `{}` in exported field `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_COMMENT =
'Missing comment for exported field `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_PARAM =
'Missing @param annotation for parameter `{}` in exported field `{}`.'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other diagnostic groups like DIAG_MISSING_GLOBAL_DOC_* and DIAG_MISSING_LOCAL_EXPORT_DOC_*, it would be better to order these new diagnostic messages as COMMENT, PARAM, then RETURN.

DIAG_MISSING_EXPORTED_METHOD_DOC_COMMENT  =
'Missing comment for exported method `{}`.'
DIAG_MISSING_EXPORTED_METHOD_DOC_PARAM    =
'Missing @param annotation for parameter `{}` in exported method `{}`.'
DIAG_MISSING_EXPORTED_METHOD_DOC_RETURN   =
'Missing @return annotation at index `{}` in exported method `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_COMMENT  =
'Missing comment for exported field `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_PARAM    =
'Missing @param annotation for parameter `{}` in exported field `{}`.'
DIAG_MISSING_EXPORTED_FIELD_DOC_RETURN   =
'Missing @return annotation at index `{}` in exported field `{}`.'

DIAG_MISSING_LOCAL_EXPORT_DOC_RETURN =
'Missing @return annotation at index `{}` in exported local function `{}`.'
DIAG_INCOMPLETE_SIGNATURE_DOC_PARAM =
Expand Down
2 changes: 2 additions & 0 deletions locale/en-us/setting.lua
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ config.diagnostics['missing-global-doc'] =
'Missing annotations for globals! Global functions must have a comment and annotations for all parameters and return values.'
config.diagnostics['missing-local-export-doc'] =
'Missing annotations for exported locals! Exported local functions must have a comment and annotations for all parameters and return values.'
config.diagnostics['missing-export-doc'] =
'Missing annotations for exported functions! Exported functions must have a comment and annotations for all parameters and return values.'
config.diagnostics['missing-parameter'] =
'Enable diagnostics for function calls where the number of arguments is less than the number of annotated function parameters.'
config.diagnostics['missing-return'] =
Expand Down
52 changes: 43 additions & 9 deletions script/core/diagnostics/helper/missing-doc-helper.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
local lang = require 'language'
local lang = require 'language'

local m = {}
local m = {}

local function findParam(docs, param)
if not docs then
Expand Down Expand Up @@ -36,14 +36,25 @@ local function findReturn(docs, index)
return false
end

local function checkFunction(source, callback, commentId, paramId, returnId)
local functionName = source.parent[1]
---@param functionName string
---@param source parser.object
---@param diagnosticRangeSource? parser.object sometimes the object with the data isn't the one that needs the diagnostics
---@param bindDocsSource? parser.object sometimes the object with the bind docs isn't the value (`a.b = c` syntax)
---@param callback fun(result: any)
---@param commentId string
---@param paramId string
---@param returnId string
local function checkFunctionNamed(functionName, source, diagnosticRangeSource, bindDocsSource, callback, commentId,
paramId, returnId)
diagnosticRangeSource = diagnosticRangeSource or source
bindDocsSource = bindDocsSource or source
local argCount = source.args and #source.args or 0
local noRealArgs = argCount == 0 or (argCount == 1 and source.args[1][1] == 'self')

if argCount == 0 and not source.returns and not source.bindDocs then
if noRealArgs and not source.returns and not bindDocsSource.bindDocs then
callback {
start = source.start,
finish = source.finish,
start = diagnosticRangeSource.start,
finish = diagnosticRangeSource.finish,
message = lang.script(commentId, functionName),
}
end
Expand All @@ -53,7 +64,7 @@ local function checkFunction(source, callback, commentId, paramId, returnId)
local argName = arg[1]
if argName ~= 'self'
and argName ~= '_' then
if not findParam(source.bindDocs, argName) then
if not findParam(bindDocsSource.bindDocs, argName) then
callback {
start = arg.start,
finish = arg.finish,
Expand All @@ -67,7 +78,7 @@ local function checkFunction(source, callback, commentId, paramId, returnId)
if source.returns then
for _, ret in ipairs(source.returns) do
for index, expr in ipairs(ret) do
if not findReturn(source.bindDocs, index) then
if not findReturn(bindDocsSource.bindDocs, index) then
callback {
start = expr.start,
finish = expr.finish,
Expand All @@ -79,5 +90,28 @@ local function checkFunction(source, callback, commentId, paramId, returnId)
end
end

---@param source parser.object
---@param callback fun(result: any)
---@param commentId string
---@param paramId string
---@param returnId string
local function checkFunction(source, callback, commentId, paramId, returnId)
local functionName = source.parent[1]
checkFunctionNamed(functionName, source, nil, nil, callback, commentId, paramId, returnId)
end


---@param source parser.object
---@param callback fun(result: any)
---@param commentId string
---@param paramId string
---@param returnId string
local function checkMethod(source, callback, commentId, paramId, returnId)
local functionName = source.method[1]
checkFunctionNamed(functionName, source.value, source, nil, callback, commentId, paramId, returnId)
end

m.CheckFunction = checkFunction
m.CheckFunctionNamed = checkFunctionNamed
m.CheckMethod = checkMethod
return m
42 changes: 42 additions & 0 deletions script/core/diagnostics/missing-export-doc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
local files = require 'files'
local guide = require 'parser.guide'
local await = require 'await'
local helper = require 'core.diagnostics.helper.missing-doc-helper'

---@async
return function (uri, callback)
local state = files.getState(uri)

if not state then
return
end

if not state.ast then
return
end

---@async
guide.eachSourceType(state.ast, 'setfield', function (source)
await.delay()
if not source.value then return end -- if the assignment is unbalanced then there is no value
if source.value.type ~= "function" then return end

-- TODO: find a better way to distinguish a.b = function and function a.b, or alternatively make them both work
-- the same way?
-- the issue is they have very similar ASTs but bindDocs is either inside or outside value

helper.CheckFunctionNamed(source.field[1], source.value, nil, source.bindDocs and source or source.value,
callback,
'DIAG_MISSING_EXPORTED_FIELD_DOC_COMMENT',
'DIAG_MISSING_EXPORTED_FIELD_DOC_PARAM',
'DIAG_MISSING_EXPORTED_FIELD_DOC_RETURN')
end)

---@async
guide.eachSourceType(state.ast, 'setmethod', function (source)
await.delay()
helper.CheckMethod(source, callback, 'DIAG_MISSING_EXPORTED_METHOD_DOC_COMMENT',
'DIAG_MISSING_EXPORTED_METHOD_DOC_PARAM',
'DIAG_MISSING_EXPORTED_METHOD_DOC_RETURN')
end)
end
1 change: 1 addition & 0 deletions script/proto/diagnostic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ m.register {
'incomplete-signature-doc',
'missing-global-doc',
'missing-local-export-doc',
'missing-export-doc',
} {
group = 'luadoc',
severity = 'Warning',
Expand Down
1 change: 1 addition & 0 deletions test/diagnostics/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ check 'lowercase-global'
check 'missing-fields'
check 'missing-global-doc'
check 'missing-local-export-doc'
check 'missing-export-doc'
check 'missing-parameter'
check 'missing-return-value'
check 'missing-return'
Expand Down
102 changes: 102 additions & 0 deletions test/diagnostics/missing-export-doc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
TEST [[
local M = {}

<!function M.f1() end!>

---comment
function M.f2()
end
]]

TEST [[
local M = {}

function M.f1(<!p!>)
end

---@param p integer
function M.f2(p)
end
]]

TEST [[
local M = {}

function M.f1()
return <!42!>
end

---@return integer
function M.f2()
return 42
end
]]

TEST [[
local M = {}

M.f1 = <!function() end!>

---comment
M.f1 = function()
end
]]

TEST [[
local M = {}

M.f1 = function(<!p!>)
end

---@param p integer
M.f1 = function(p)
end
]]

TEST [[
local M = {}

M.f1 = function()
return <!42!>
end

---@return integer
M.f2 = function()
return 42
end
]]


TEST [[
local M = {}

function <!M:f1!>() end

---comment
function M:f2()
end
]]

TEST [[
local M = {}

function M:f1(<!p!>)
end

---@param p integer
function M:f2(p)
end
]]

TEST [[
local M = {}

function M:f1()
return <!42!>
end

---@return integer
function M:f2()
return 42
end
]]
Loading