Benjamin-Dobell / IntelliJ-Luanalysis

Type-safe Lua IDE — IntelliJ IDEA plugin
Apache License 2.0
154 stars 21 forks source link

Better support optional arguments without spamming `@overload` #125

Closed LoganDark closed 1 year ago

LoganDark commented 2 years ago

Luanalysis differentiates between missing arguments, and present arguments that are specified as nil, which is the correct behavior (especially when calling C functions). However, this also means that functions with true optional arguments have to be typed like this:

--- @param subject string
--- @param regex string
--- @param options string
--- @param ctx table
--- @param res_table table
--- @return ngx__CaptureResult | nil | (nil, string)
--- @overload fun(subject: string, regex: string, options: string, ctx: table): ngx__CaptureResult | nil | (nil, string)
--- @overload fun(subject: string, regex: string, options: string): ngx__CaptureResult | nil | (nil, string)
--- @overload fun(subject: string, regex: string): ngx__CaptureResult | nil | (nil, string)
function re.match(subject, regex, options, ctx, res_table) end

Notice that I have to add three additional overloads to specify that some arguments are optional. Optional arguments don't accept nils, but can optionally not be present to use the default.

Supporting syntax like @param name? (specifically the ? to indicate optional) would be helpful here. Additionally supporting a postfix ? operator on types to mean | nil would also be helpful.

--- @param reused_session ngx__SslSession
--- @param server_name string
--- @param ssl_verify boolean
--- @param send_status_req boolean
--- @overload fun(reused_session: false, server_name: string, ssl_verify: boolean, send_status_req: boolean): true | (false, string)
--- @overload fun(reused_session: false, server_name: string, ssl_verify: boolean): true | (false, string)
--- @overload fun(reused_session: false, server_name: string): true | (false, string)
--- @overload fun(reused_session: false): true | (false, string)
--- @overload fun(reused_session: ngx__SslSession, server_name: string, ssl_verify: boolean, send_status_req: boolean): ngx__SslSession | (nil, string)
--- @overload fun(reused_session: ngx__SslSession, server_name: string, ssl_verify: boolean): ngx__SslSession | (nil, string)
--- @overload fun(reused_session: ngx__SslSession, server_name: string): ngx__SslSession | (nil, string)
--- @overload fun(reused_session: ngx__SslSession): ngx__SslSession | (nil, string)
--- @overload fun(): ngx__SslSession | (nil, string)
--- @return ngx__SslSession | (nil, string)
function TcpSocket:sslhandshake(reused_session, server_name, ssl_verify, send_status_req) end

nine overloads because certain arguments like reused_session change the return type when they are used. :/ (supporting ? after parameters in fun expressions would help with declaring that as well, like fun(param?: type))

Benjamin-Dobell commented 1 year ago

Closed by 5d721ed99d75f0f9a797512ad91d57bd99777ddd (and a few prior commits)

Release will be published in the next 12 hours. However, this example becomes:

---@class ngx__SslSession

---@class TcpSocket
local TcpSocket = {}

--- @param reused_session false | ngx__SslSession
--- @param server_name string
--- @param ssl_verify boolean
--- @param send_status_req boolean
--- @return ngx__SslSession | (nil, string)
--- @overload fun(reused_session: false, server_name?: string, ssl_verify?: boolean, send_status_req?: boolean): true | (false, string)
--- @overload fun(reused_session: ngx__SslSession, server_name?: string, ssl_verify?: boolean, send_status_req?: boolean): ngx__SslSession | (nil, string)
function TcpSocket:sslhandshake(reused_session, server_name, ssl_verify, send_status_req)
    return nil, ""
end

---@type ngx__SslSession
local session

---@type TcpSocket
local socket

local new_session, reused_error = socket:sslhandshake(session)
local success, fresh_error = socket:sslhandshake(false)