swc-project / swc

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

lookup_char_* can fail on certain type of Typescript node types (enum, etcs) #5535

Open kwonoj opened 2 years ago

kwonoj commented 2 years ago

Current bahavior

To allow optimizing binary size, SWC invokes custom transforms (for custom binary) and plugins after applying resolver, decorators, typescript::strip.

This had great impact on reducing binary size, but because of this plugin or custom transforms may accept BytePos(0), which is reserved for compiler-generated nodes, for spans of those AST nodes.

Currently, changing it to pass AST to plugin before applying decorators is considered. But if we do so the plugins which use noop_visit_mut_type!() for size optimization will be broken.

kwonoj commented 2 years ago

@kdy1 Thoughts on how to resolve this? it blocks coverage instrumentation for some typescript files.

kdy1 commented 2 years ago

Maybe we can pass the whole AST with typescript types to the plugin but then plugins using noop_visit_mut for optimization will be broken.

kwonoj commented 2 years ago

Maybe plugin would silently ignore these, in most cases lookup source would be needed for the real codes only. In that case the issue is lookup_char traps if it fails to lookup - which plugin does not have way to gracefully handle this at all.

sam-goodwin commented 1 year ago

this plugin or custom transforms may accept BytePos(0)

In what cases will this happen? Can plugins rely on the spans correctly mapping back to the source?

kwonoj commented 1 year ago

In what cases will this happen?

If plugin receives already transformed AST from prior transform / or core transforms which have dummy spans.

sam-goodwin commented 1 year ago

If plugin receives already transformed AST from prior transform

Not precisely sure what you mean by this. Below is my configuration - I only have my plugin configured, but I'm transforming typescript to JS using the built-in SWC transform. Will the spans my plugin sees map correctly back to the source that created them? It's not clear to me if the ts->js transform runs before or after my plugin - I assume before.

{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "dynamicImport": false,
      "decorators": false,
      "hidden": {
        "jest": true
      }
    },
    "transform": null,
    "target": "es2021",
    "loose": false,
    "externalHelpers": false,
    "experimental": {
      "plugins": [
        [
          "<my-plugin>",
          {}
        ]
      ]
    }
  },
  "minify": true,
  "sourceMaps": "inline",
  "module": {
    "type": "commonjs"
  }
}
kwonoj commented 1 year ago

That is the case issue's main body describes as core transform strips out ts codes.

sam-goodwin commented 1 year ago

Thanks for the quick responses!

Ok, so just to be clear. A plugin can rely on mapping a Span to the LineCol when the Span is not at BytePos(0). In this case, the LineCol will point to the code within the original TypeScript code. This would be the expected behavior and I can't imagine it being useful any other way.

kwonoj commented 1 year ago

A plugin can rely on mapping a Span to the LineCol when the Span is not at BytePos(0)

Yes, that's my understanding as well.

This would be the expected behavior and I can't imagine it being useful any other way.

I'd say yes for the expected behavior, not sure being useful any other way exactly means.

sam-goodwin commented 1 year ago

Thank you - that clears things up for me.

not sure being useful any other way exactly means

Oh - I just meant that it wouldn't make sense if a non-zero Span didn't point back to source that created that node.