Skip to content

Commit c40b6fa

Browse files
authored
respect @noself on interface containing call signature (#1724)
* respect @noself on interface containing call signature * replace @noself call-signature snapshot tests with runtime argc probes
1 parent 4855d04 commit c40b6fa

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/transformation/utils/function-context.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,19 @@ function computeDeclarationContextType(context: TransformationContext, signature
143143
return ContextType.NonVoid;
144144
}
145145

146+
// Call signature inside a class or interface respects @noSelf on the enclosing class/interface
147+
if (ts.isCallSignatureDeclaration(signatureDeclaration)) {
148+
const scopeDeclaration = findFirstNodeAbove(
149+
signatureDeclaration,
150+
(n): n is ts.ClassLikeDeclaration | ts.InterfaceDeclaration =>
151+
ts.isClassDeclaration(n) || ts.isClassExpression(n) || ts.isInterfaceDeclaration(n)
152+
);
153+
154+
if (scopeDeclaration !== undefined && getNodeAnnotations(scopeDeclaration).has(AnnotationKind.NoSelf)) {
155+
return ContextType.Void;
156+
}
157+
}
158+
146159
// When using --noImplicitSelf and the signature is defined in a file targeted by the program apply the @noSelf rule.
147160
const program = context.program;
148161
const options = program.getCompilerOptions() as CompilerOptions;

test/unit/functions/noSelfAnnotation.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,50 @@ test("@noSelf on static class methods with string key access", () => {
6565
`.expectLuaToMatchSnapshot();
6666
});
6767

68+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1661
69+
// A Lua-side function observes the actual argc, so a missing @noSelf would
70+
// surface as a phantom leading nil (argc 2 instead of 1).
71+
const argcProbeHeader = `
72+
function probe(...)
73+
return select("#", ...)
74+
end
75+
`;
76+
77+
test("@noSelf on interface call signature: Lua probe sees correct argc", () => {
78+
util.testModule`
79+
/** @noSelf */
80+
interface Probe { (a: string): number; }
81+
declare const probe: Probe;
82+
export const result = probe("hi");
83+
`
84+
.setLuaHeader(argcProbeHeader)
85+
.expectToEqual({ result: 1 });
86+
});
87+
88+
test("@noSelf parent interface, property typed by call-signature interface: Lua probe sees correct argc", () => {
89+
util.testModule`
90+
/** @noSelf */
91+
interface CallSignature { (a: string): number; }
92+
/** @noSelf */
93+
interface Holder { fn: CallSignature; }
94+
declare const holder: Holder;
95+
export const result = holder.fn("hi");
96+
`
97+
.setLuaHeader(`${argcProbeHeader}\nholder = { fn = probe }`)
98+
.expectToEqual({ result: 1 });
99+
});
100+
101+
test("@noSelf parent interface, property typed by type-literal call signature: Lua probe sees correct argc", () => {
102+
util.testModule`
103+
/** @noSelf */
104+
interface Holder { fn: { (a: string): number }; }
105+
declare const holder: Holder;
106+
export const result = holder.fn("hi");
107+
`
108+
.setLuaHeader(`${argcProbeHeader}\nholder = { fn = probe }`)
109+
.expectToEqual({ result: 1 });
110+
});
111+
68112
// additional coverage for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1292
69113
test("explicit this parameter respected over @noSelf", () => {
70114
util.testModule`

0 commit comments

Comments
 (0)