From a08d71f13e54eba17bc2f6234bfe564db9545f54 Mon Sep 17 00:00:00 2001 From: hyeonsang Date: Fri, 28 Aug 2026 19:40:52 +0900 Subject: [PATCH] Disallow optional calls on import.defer --- tsc/internal/parser/parser.go | 3 +++ .../importDeferOptionalCall.errors.txt | 16 +++++++++++++ .../conformance/importDeferOptionalCall.js | 18 +++++++++++++++ .../importDeferOptionalCall.symbols | 16 +++++++++++++ .../conformance/importDeferOptionalCall.types | 23 +++++++++++++++++++ .../importDefer/importDeferOptionalCall.ts | 9 ++++++++ 6 files changed, 85 insertions(+) create mode 100644 tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.errors.txt create mode 100644 tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.js create mode 100644 tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.symbols create mode 100644 tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.types create mode 100644 tsc/testdata/tests/cases/conformance/importDefer/importDeferOptionalCall.ts diff --git a/tsc/internal/parser/parser.go b/tsc/internal/parser/parser.go index 8b53a39b0cda3..a49ccfef55f1f 100644 --- a/tsc/internal/parser/parser.go +++ b/tsc/internal/parser/parser.go @@ -5241,6 +5241,9 @@ func (p *Parser) parseLeftHandSideExpressionOrHigher() *ast.Expression { p.nextToken() // advance past the dot expression = p.finishNode(p.factory.NewMetaProperty(ast.KindImportKeyword, p.parseIdentifierName()), pos) if expression.Text() == "defer" { + if p.token == ast.KindQuestionDotToken && p.lookAhead((*Parser).nextTokenIsOpenParenOrLessThan) { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindOpenParenToken)) + } if p.token == ast.KindOpenParenToken || p.token == ast.KindLessThanToken { p.sourceFlags |= ast.NodeFlagsPossiblyContainsDynamicImport } diff --git a/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.errors.txt b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.errors.txt new file mode 100644 index 0000000000000..e99e2334ca075 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.errors.txt @@ -0,0 +1,16 @@ +b.ts(1,13): error TS1005: '(' expected. +b.ts(2,13): error TS1005: '(' expected. + + +==== x.ts (0 errors) ==== + export const x = 1; + +==== b.ts (2 errors) ==== + import.defer?.("./x"); + ~~ +!!! error TS1005: '(' expected. + import.defer?.("./x"); + ~~ +!!! error TS1005: '(' expected. + import.defer("./x"); + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.js b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.js new file mode 100644 index 0000000000000..86e22802e0793 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.js @@ -0,0 +1,18 @@ +//// [tests/cases/conformance/importDefer/importDeferOptionalCall.ts] //// + +//// [x.ts] +export const x = 1; + +//// [b.ts] +import.defer?.("./x"); +import.defer?.("./x"); +import.defer("./x"); + + +//// [x.js] +export const x = 1; +//// [b.js] +"use strict"; +import.defer?.("./x"); +import.defer?.("./x"); +import.defer("./x"); diff --git a/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.symbols b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.symbols new file mode 100644 index 0000000000000..1c6d788e36628 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.symbols @@ -0,0 +1,16 @@ +//// [tests/cases/conformance/importDefer/importDeferOptionalCall.ts] //// + +=== x.ts === +export const x = 1; +>x : Symbol(x, Decl(x.ts, 0, 12)) + +=== b.ts === +import.defer?.("./x"); +>"./x" : Symbol("./x", Decl(x.ts, 0, 0)) + +import.defer?.("./x"); +>"./x" : Symbol("./x", Decl(x.ts, 0, 0)) + +import.defer("./x"); +>"./x" : Symbol("./x", Decl(x.ts, 0, 0)) + diff --git a/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.types b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.types new file mode 100644 index 0000000000000..cea9d6cf54487 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importDeferOptionalCall.types @@ -0,0 +1,23 @@ +//// [tests/cases/conformance/importDefer/importDeferOptionalCall.ts] //// + +=== x.ts === +export const x = 1; +>x : 1 +>1 : 1 + +=== b.ts === +import.defer?.("./x"); +>import.defer?.("./x") : Promise +>defer : any +>"./x" : "./x" + +import.defer?.("./x"); +>import.defer?.("./x") : Promise +>defer : any +>"./x" : "./x" + +import.defer("./x"); +>import.defer("./x") : Promise +>defer : any +>"./x" : "./x" + diff --git a/tsc/testdata/tests/cases/conformance/importDefer/importDeferOptionalCall.ts b/tsc/testdata/tests/cases/conformance/importDefer/importDeferOptionalCall.ts new file mode 100644 index 0000000000000..650fec4838c50 --- /dev/null +++ b/tsc/testdata/tests/cases/conformance/importDefer/importDeferOptionalCall.ts @@ -0,0 +1,9 @@ +// @module: esnext + +// @filename: x.ts +export const x = 1; + +// @filename: b.ts +import.defer?.("./x"); +import.defer?.("./x"); +import.defer("./x");