swc-project / swc

Rust-based platform for the Web
https://swc.rs
Apache License 2.0
30.54k stars 1.18k forks source link

@swc/helper _ts_generator wrong code exported #9110

Open yf-yang opened 1 week ago

yf-yang commented 1 week ago

Describe the bug

@swc/helpers unexpectedly merges two different code paths.

Input code

function* test() {
  while (!False()) {
    // execute this line
    while (!False()) {
      // execute this line
      break;
    }
    // execute this line
    if (False()) {
      // NOT execute this line
      break;
    }

    // execute this line
    yield "correct";
    return;
  }

  // NOT execute this line
  yield "wrong";
  return;
}

function False() {
  return false;
}

for (const result of test()) {
  console.log(result);
}

Config

{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "externalHelpers": true
  },
  "env": {
    "targets": "Chrome >= 48"
  }
}

Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.6.5&code=H4sIAAAAAAAAA42RPQ7CMAyFd07x6JQy0AOwM8LCBSA4bUSUSImjghB3p%2Flhg4gt8Xvfs2WraCVrZzdgCix6PFfAPGlDEOv92QQSfSkCwwC6k4xM4EkHGG0pCz%2F8DQK4eDrfdvnzaqdrBfEt%2BXA8%2FZfejn9oMld00nlPkrsCeeLobXpnvNGt4rN3dszwB11AVZeLOn6evuhQqVRczkNIZwMvWoiG4VS9RiGS5gxtjRtFcfQJfANPEoyouwEAAA%3D%3D&config=H4sIAAAAAAAAA1WPSw7DIAwF9zkF8rrbdtE79BCIOhERP9mOVBTl7oUE0maH3xszsA5KwcwGnmotxzIkTYx0ziXhHER%2FSgKSE7IhmwRuvRWu1agd4x5tRwOiaUKpW8j3hoOLkbHjLfM22DH%2FC030iZD5ClZUh8nhVTc0Jfj4XvayfaQ%2B9tA%2F4Ad12XkxWH71TaEFh%2B0LYuVI0xQBAAA%3D

SWC Info output

Operating System: Platform: darwin Arch: x64 Machine Type: x86_64 Version: Darwin Kernel Version 22.3.0: Mon Jan 30 20:42:11 PST 2023; root:xnu-8792.81.3~2/RELEASE_X86_64 CPU: (16 cores) Models: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz

Binaries:
    Node: 20.9.0
    npm: 10.1.0
    Yarn: 1.22.19
    pnpm: 9.4.0

Relevant Packages:
    @swc/core: 1.6.5
    @swc/helpers: 0.5.11
    @swc/types: N/A

SWC Config:
    output: N/A
    .swcrc path: N/A

Next.js info:
    output: N/A

Expected behavior

The two break should not be merged together and yields "correct"

Actual behavior

Two break are merged, inner break directly breaks two while loops and yields "wrong"

Version

@swc/helpers: 0.5.11

Additional context

You can also play with https://github.com/yf-yang/swc-ts-generator-bug, but maybe the playground is enough.

pnpm i
pnpm swc src/index.ts -o index.js
pnpm tsx src/index.ts # correct
pnpm tsx index.js # wrong
kdy1 commented 1 week ago

Related link: https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformers/generators.ts

Investigation

tsc:

function test() {
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                if (!!False()) return [3 /*break*/, 2];
                // execute this line

                // >>>>> >>>>>

                while (!False()) {
                    // execute this line
                    break;
                }
                // execute this line
                if (False()) {
                    // NOT execute this line
                    return [3 /*break*/, 2];
                }
                // execute this line
                return [4 /*yield*/, "correct"];
            case 1:
                // execute this line
                _a.sent();
                return [2 /*return*/];
            case 2:
                // NOT execute this line
                return [4 /*yield*/, "wrong"];
            case 3:
                // NOT execute this line
                _a.sent();
                return [2 /*return*/];
        }
    });
}

swc:

function test() {
    return _ts_generator(this, function(_state) {
        switch(_state.label){
            case 0:
                if (!!False()) return [
                    3,
                    2
                ];

                // execute this line

                // >>>>> >>>>>
                while(!False()){
                    // execute this line
                    return [
                        3,
                        2
                    ];
                }
                // execute this line
                if (False()) {
                    // NOT execute this line
                    return [
                        3,
                        2
                    ];
                }
                // execute this line
                return [
                    4,
                    "correct"
                ];
            case 1:
                _state.sent();
                return [
                    2
                ];
            case 2:
                // NOT execute this line
                return [
                    4,
                    "wrong"
                ];
            case 3:
                _state.sent();
                return [
                    2
                ];
        }
    });
}