love2d-community / love-api

The whole LÖVE wiki in a Lua table.
http://love2d-community.github.io/love-api/
304 stars 48 forks source link

Lua 5.4's `require` corrupts tables #121

Closed davisdude closed 1 year ago

davisdude commented 1 year ago

As of Lua 5.4, require returns a second value.

Docs of require for Lua 5.3:

Once a loader is found, require calls the loader with two arguments: modname and an extra value dependent on how it got the loader. (If the loader came from a file, this extra value is the file name.) If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname]. If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the final value of package.loaded[modname].

Docs of require for Lua 5.4 (emphasis mine)

Once a loader is found, require calls the loader with two arguments: modname and an extra value, a loader data, also returned by the searcher. The loader data can be any value useful to the module; for the default searchers, it indicates where the loader was found. (For instance, if the loader came from a file, this extra value is the file path.) If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname]. If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the final value of package.loaded[modname]. Besides that value, require also returns as a second result the loader data returned by the searcher, which indicates how require found the module.

If using Lua 5.4 or greater, this breaks many scripts that rely on each module being a valid table object. This is because many of the tables, e.g. types / enums, are just made by calling require on the constituent objects. For example an example of how this breaks things, see, vim-love-docs.

MikuAuahDark commented 1 year ago

Hello,

Do you have proposed solution on how to fix this problem?

davisdude commented 1 year ago

There are two possible ways that I've thought of. Each has been tested with the following script:

local api = require('love_api')
print(#api.modules[1].types)

Currently, using Lua 5.4, this script will return 3, unless one of the fixes is used, in which case it returns 2, as expected.

  1. Put nil after the last require of each table, preventing the second value from coming through. E.g.

     local path = (...):match('(.-)[^%./]+$')
    
     return {
         name = 'audio',
         description = 'Provides an interface to create noise with the user\'s speakers.',
         types = {
             require(path .. 'types.RecordingDevice'),
             require(path .. 'types.Source'),
    +        nil,
         },
          -- ...
  2. Assign all required variables outside of table creation, allowing the second return value to be dropped. E.g.

     local path = (...):match('(.-)[^%./]+$')
    
    +local RecordingDevice = require(path .. 'types.RecordingDevice')
    +local Source = require(path .. 'types.Source')
    
     return {
         name = 'audio',
         description = 'Provides an interface to create noise with the user\'s speakers.',
         types = {
    -        require(path .. 'types.RecordingDevice'),
    +        RecordingDevice,
    -        require(path .. 'types.Source'),
    +        Source,
         },
        -- ...

You could also monkey-patch and/or write your own require function that always tosses the second return value, but that I'm not a huge fan of that approach.

MikuAuahDark commented 1 year ago

What about surrounding the require in a parenthesis such as (require(path .. 'types.RecordingDevice'))?

davisdude commented 1 year ago

That would also work - there are a few ways this could be addressed.