diff --git a/generated/secondlife.d.luau b/generated/secondlife.d.luau index 1c8792ed..83853dcf 100644 --- a/generated/secondlife.d.luau +++ b/generated/secondlife.d.luau @@ -230,27 +230,27 @@ export type PrimParamsSetterType = typeof( ) -declare function assert(value: T?, errorMessage: string?): T +-- declare function assert(value: T?, errorMessage: string?): T -- magic type declare function dangerouslyexecuterequiredmodule(f: (...any) -> ...any): ...any declare function error(message: T, level: number?): never declare function gcinfo(): number declare getfenv: nil -declare function getmetatable(obj: T): getmetatable +-- declare function getmetatable(obj: T): getmetatable -- builtin declare function ipairs(tab: {V}): (({V}, number) -> (number?, V), {V}, number) declare loadstring: nil declare function newproxy(mt: boolean?): any -declare function next(t: {[K]: V}, i: K?): (K?, V) -declare function pairs(t: {[K]: V}): (({[K]: V}, K?) -> (K?, V), {[K]: V}, K) +-- declare function next(t: {[K]: V}, i: K?): (K?, V) -- builtin +-- declare function pairs(t: {[K]: V}): (({[K]: V}, K?) -> (K?, V), {[K]: V}, K) -- builtin declare function pcall(f: (A...) -> R..., ...: A...): (boolean, R...) declare function print(...: T...): () declare function rawequal(a: T1, b: T2): boolean declare function rawget(t: {[K]: V}, k: K): V? declare function rawlen(t: {[K]: V} | string): number declare function rawset(t: {[K]: V}, k: K, v: V): {[K]: V} -declare function require(target: any): any -declare function select(i: string | number, ...: A...): ...any +-- declare function require(target: any): any -- magic type +-- declare function select(i: string | number, ...: A...): ...any -- magic type declare setfenv: nil -declare function setmetatable(t: T, mt: MT): setmetatable +-- declare function setmetatable(t: T, mt: MT): setmetatable -- builtin, magic type declare function tonumber(value: string? | number, base: number?): number? declare function toquaternion(value: string? | quaternion): quaternion? declare function torotation(value: string? | quaternion): quaternion? @@ -466,31 +466,33 @@ declare quaternion: ((x: number, y: number, z: number, s: number) -> quaternion) toup: (q: quaternion) -> vector, } +--[[ commented out to avoid shadowing magic type functions find, format, gmatch, and match --------------------------- -- Global Table: string --------------------------- declare string: { - byte: (s: string, i: number?, j: number?) -> ...number, - char: (...number) -> string, - find: (s: string, pattern: string, init: number?, plain: boolean?) -> (number?, number?, ...string), - format: (formatstring: string, ...any) -> string, - gmatch: (s: string, pattern: string) -> () -> ...string, - gsub: (s: string, pattern: string, repl: string | { [string]: string } | (...string) -> string, maxn: number?) -> (string, number), - len: (s: string) -> number, - lower: (s: string) -> string, - match: (s: string, pattern: string, init: number?) -> ...string, - pack: (fmt: string, ...any) -> string, - packsize: (fmt: string) -> number, - rep: (s: string, n: number) -> string, - reverse: (s: string) -> string, - split: (s: string, separator: string?) -> {string}, - sub: (s: string, i: number, j: number?) -> string, - unpack: (fmt: string, s: string, init: number?) -> ...any, - upper: (s: string) -> string, + byte: (s: string, i: number?, j: number?) -> ...number, -- builtin + char: (...number) -> string, -- builtin + find: (s: string, pattern: string, init: number?, plain: boolean?) -> (number?, number?, ...string), -- builtin, magic type + format: (formatstring: string, ...any) -> string, -- builtin, magic type + gmatch: (s: string, pattern: string) -> () -> ...string, -- builtin, magic type + gsub: (s: string, pattern: string, repl: string | { [string]: string } | (...string) -> string, maxn: number?) -> (string, number), -- builtin + len: (s: string) -> number, -- builtin + lower: (s: string) -> string, -- builtin + match: (s: string, pattern: string, init: number?) -> ...string, -- builtin, magic type + pack: (fmt: string, ...any) -> string, -- builtin + packsize: (fmt: string) -> number, -- builtin + rep: (s: string, n: number) -> string, -- builtin + reverse: (s: string) -> string, -- builtin + split: (s: string, separator: string?) -> {string}, -- builtin + sub: (s: string, i: number, j: number?) -> string, -- builtin + unpack: (fmt: string, s: string, init: number?) -> ...any, -- builtin + upper: (s: string) -> string, -- builtin } +--]] --------------------------- -- Global Table: table @@ -508,16 +510,16 @@ declare table: { extend: (a: {V}, b: {V}) -> {V}, remove: (a: {V}, i: number?) -> V?, sort: (a: {V}, f: ((a: V, b: V) -> boolean)?) -> (), - pack: (...V) -> { n: number, [number]: V }, + pack: (...V) -> { n: number, [number]: V }, -- magic type unpack: (a: {V}, i: number?, j: number?) -> ...V, move: (src: {V}, i: number, j: number, d: number, dest: {V}?) -> {V}, create: (n: number, v: V?) -> {V}, find: (t: {V}, v: V, i: number?) -> number?, clear: (t: {}) -> (), shrink: (t: {V}, shrink_sparse: boolean?) -> {V}, - freeze: (t: table) -> table, + freeze:
(t: table) -> table, -- magic type isfrozen: (t: {}) -> boolean, - clone:
(t: table) -> table, + clone:
(t: table) -> table, -- magic type } diff --git a/lsl_definitions/generators/slua.py b/lsl_definitions/generators/slua.py index 229bebb8..1181fc1e 100644 --- a/lsl_definitions/generators/slua.py +++ b/lsl_definitions/generators/slua.py @@ -54,12 +54,20 @@ def gen_luau_lsp_defs(definitions: LSLDefinitions, slua_definitions: SLuaDefinit for func in slua_definitions.functions: if func.private or func.local_only: continue + if not func.typechecker_flags.fully_defined: + defs.write("-- ") defs.write("declare ") func.write_luau_global_def(defs) for module in sorted(slua_definitions.modules, key=lambda x: x.name): if module.name in {"ll", "llcompat"}: continue + if module.name == "string": + defs.write( + "--[[ commented out to avoid shadowing magic type functions find, format, gmatch, and match\n" + ) module.write_luau_def(defs) + if module.name == "string": + defs.write("--]]\n") for var in slua_definitions.global_variables: defs.write("declare ") defs.write(var.to_luau_def()) diff --git a/lsl_definitions/slua.py b/lsl_definitions/slua.py index 496fd996..693c118f 100644 --- a/lsl_definitions/slua.py +++ b/lsl_definitions/slua.py @@ -76,6 +76,34 @@ def to_luau_def(self, declaration: bool = False) -> str: return f"{self.name}: {self.type}" +@dataclasses.dataclass +class SLuaTypecheckerFlags: + """Flags specific to the internals of luau-analyze and luau-lsp.""" + + builtin: bool = False + """This function is defined in BuiltinDefinitions.cpp, rather than EmbeddedBuiltinDefinitions.cpp.""" + magic: bool = False + """ + The typechecker has custom logic for this function. + For examples of each magic type function, see the comments of + https://github.com/secondlife/lsl-definitions/pull/130 + """ + + @property + def fully_defined(self) -> bool: + """True if this function is fully defined and won't cause issues for the typechecker.""" + return not self.builtin and not self.magic + + @property + def comment_string(self) -> str: + comments = [] + if self.builtin: + comments.append("builtin") + if self.magic: + comments.append("magic type") + return " -- " + ", ".join(comments) if comments else "" + + @dataclasses.dataclass class SLuaFunctionBase(abc.ABC): name: str = "" @@ -115,6 +143,10 @@ class SLuaFunction(SLuaFunctionBase): must_use: bool = False """Emit a warning if the return value is not used. See https://kampfkarren.github.io/selene/usage/std.html#must_use.""" + typechecker_flags: SLuaTypecheckerFlags = dataclasses.field( + default_factory=SLuaTypecheckerFlags + ) + """Flags specific to the internals of luau-analyze and luau-lsp.""" overloads: List[SLuaFunctionOverload] = dataclasses.field(default_factory=list) @property @@ -164,7 +196,9 @@ def write_luau_global_def(self, f: TextIO, indent: int = 0) -> None: f.write(f"function {self.name}") f.write(self.type_parameters_string) f.write(self.parameters_string(declaration=True)) - f.write(f": {self.return_type}\n") + f.write(f": {self.return_type}") + f.write(self.typechecker_flags.comment_string) + f.write("\n") def write_luau_table_def(self, f: TextIO, indent: int = 0, suffix=",") -> None: """For declaring functions within a table/module""" @@ -180,6 +214,7 @@ def write_luau_table_def(self, f: TextIO, indent: int = 0, suffix=",") -> None: f.write(overload.type_def_string) f.write(")") f.write(suffix) + f.write(self.typechecker_flags.comment_string) f.write("\n") @@ -770,6 +805,7 @@ def _validate_function( local_only=data.get("local-only", False), slua_removed=data.get("slua-removed", False), must_use=data.get("must-use", False), + typechecker_flags=SLuaTypecheckerFlags(**data.get("typechecker", {})), ) self._validate_identifier(func.name) self._validate_scope(func.name, scope) diff --git a/slua_definitions.schema.json b/slua_definitions.schema.json index 50e70c54..19f8f016 100644 --- a/slua_definitions.schema.json +++ b/slua_definitions.schema.json @@ -190,6 +190,23 @@ "comment": { "markdownDescription": "A brief description of this function.", "type": "string" + }, + "typechecker": { + "type": "object", + "description": "Flags specific to the internals of luau-analyze and luau-lsp.", + "additionalProperties": false, + "properties": { + "builtin": { + "const": true, + "description": "This function is defined in BuiltinDefinitions.cpp, rather than EmbeddedBuiltinDefinitions.cpp.", + "type": "boolean" + }, + "magic": { + "const": true, + "description": "The typechecker has custom logic for this function. Look for attachMagicFunction in the source code.", + "type": "boolean" + } + } } }, "required": ["name"], diff --git a/slua_definitions.yaml b/slua_definitions.yaml index 18b1dc79..493b6639 100644 --- a/slua_definitions.yaml +++ b/slua_definitions.yaml @@ -359,6 +359,7 @@ functions: type: string? return-type: T fastcall: true + typechecker: {magic: true} - name: collectgarbage comment: Run the garbage collector parameters: @@ -410,6 +411,7 @@ functions: type: T selene-type: table return-type: getmetatable + typechecker: {builtin: true} # builtin due to FFlag::LuauSolverV2 fastcall: true - name: graphheap comment: Writes the contents of heap to the given file in JSON format. @@ -461,6 +463,7 @@ functions: comment: Optional key to start traversal after. type: K? return-type: (K?, V) + typechecker: {builtin: true} # builtin due to FFlag::LuauStorePolarityInline - name: pairs comment: Returns an iterator for all key-value pairs in the table. type-parameters: [K, V] @@ -469,6 +472,7 @@ functions: comment: The table to iterate over. type: "{[K]: V}" return-type: "(({[K]: V}, K?) -> (K?, V), {[K]: V}, K)" + typechecker: {builtin: true} # builtin due to FFlag::LuauStorePolarityInline - name: pcall comment: Calls function f with parameters args, returning success and function results or an error. type-parameters: [A..., R...] @@ -543,6 +547,7 @@ functions: comment: The name of the external module. type: any return-type: any + typechecker: {magic: true} - name: select comment: Returns a subset of arguments or the number of arguments. type-parameters: [A...] @@ -555,6 +560,7 @@ functions: type: A... return-type: ...any fastcall: true + typechecker: {magic: true} - name: setfenv comment: Set the scoped environment for the given function. type-parameters: [T..., R...] @@ -580,6 +586,7 @@ functions: selene-type: table return-type: setmetatable fastcall: true + typechecker: {builtin: true, magic: true} # builtin due to FFlag::LuauSolverV2 - name: tonumber comment: Converts the input string to a number in the specified base. parameters: @@ -2036,6 +2043,7 @@ modules: return-type: '...number' must-use: true fastcall: true + typechecker: {builtin: true} - name: char comment: Returns a string containing characters for the given byte values. parameters: @@ -2046,6 +2054,7 @@ modules: return-type: string must-use: true fastcall: true + typechecker: {builtin: true} - name: find comment: Finds the first instance of the pattern in the string. parameters: @@ -2063,6 +2072,7 @@ modules: type: boolean? return-type: (number?, number?, ...string) must-use: true + typechecker: {builtin: true, magic: true} - name: format comment: Formats input values into a string using printf-style format specifiers. parameters: @@ -2075,6 +2085,7 @@ modules: optional: false return-type: string must-use: true + typechecker: {builtin: true, magic: true} - name: gmatch comment: Returns an iterator function for pattern matches parameters: @@ -2086,6 +2097,7 @@ modules: type: string return-type: () -> ...string must-use: true + typechecker: {builtin: true, magic: true} - name: gsub comment: Performs pattern-based substitution in a string. parameters: @@ -2103,6 +2115,7 @@ modules: type: number? return-type: (string, number) must-use: true + typechecker: {builtin: true} - name: len comment: "Returns the number of bytes in the string. Identical to #s" parameters: @@ -2112,6 +2125,7 @@ modules: return-type: number must-use: true fastcall: true + typechecker: {builtin: true} - name: lower comment: Returns a lowercase version of the input string. Only works with ASCII. Use ll.ToLower for strings that may contain Unicode. parameters: @@ -2120,6 +2134,7 @@ modules: type: string return-type: string must-use: true + typechecker: {builtin: true} - name: match comment: Finds and returns matches for a pattern in the input string. parameters: @@ -2134,6 +2149,7 @@ modules: type: number? return-type: '...string' must-use: true + typechecker: {builtin: true, magic: true} - name: pack comment: Packs values into a binary string. parameters: @@ -2145,6 +2161,7 @@ modules: type: ...any return-type: string must-use: true + typechecker: {builtin: true} - name: packsize comment: Returns the size of a packed string for the given format. parameters: @@ -2153,6 +2170,7 @@ modules: type: string return-type: number must-use: true + typechecker: {builtin: true} - name: rep comment: Returns the input string repeated a given number of times. parameters: @@ -2164,6 +2182,7 @@ modules: type: number return-type: string must-use: true + typechecker: {builtin: true} - name: reverse comment: Returns the input string with bytes in reverse order. parameters: @@ -2172,6 +2191,7 @@ modules: type: string return-type: string must-use: true + typechecker: {builtin: true} - name: split comment: Splits a string by separator. Returns a list of substrings. parameters: @@ -2183,6 +2203,7 @@ modules: type: string? return-type: '{string}' must-use: true + typechecker: {builtin: true} - name: sub comment: Returns a substring from the given range. parameters: @@ -2198,6 +2219,7 @@ modules: return-type: string must-use: true fastcall: true + typechecker: {builtin: true} - name: unpack comment: Decodes a binary string using a pack format. parameters: @@ -2212,6 +2234,7 @@ modules: type: number? return-type: '...any' must-use: true + typechecker: {builtin: true} - name: upper comment: Returns an uppercase version of the input string. Only works with ASCII. Use ll.ToUpper for strings that may contain Unicode. parameters: @@ -2220,6 +2243,7 @@ modules: type: string return-type: string must-use: true + typechecker: {builtin: true} - name: table comment: Table manipulation library. Tables are collections of key-value pairs. functions: @@ -2372,6 +2396,7 @@ modules: type: ...V return-type: '{ n: number, [number]: V }' must-use: true + typechecker: {magic: true} - name: unpack comment: Unpacks array elements into multiple return values. type-parameters: [V] @@ -2464,6 +2489,7 @@ modules: type: table # should be: generic table. See #56 selene-type: table return-type: table + typechecker: {magic: true} - name: isfrozen comment: Returns true if a table is frozen. parameters: @@ -2482,6 +2508,7 @@ modules: selene-type: table return-type: table must-use: true + typechecker: {magic: true} - name: utf8 comment: UTF-8 support library. functions: