Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect Span on BindingIdentifier #4739

Closed
DonIsaac opened this issue Aug 7, 2024 · 9 comments
Closed

Incorrect Span on BindingIdentifier #4739

DonIsaac opened this issue Aug 7, 2024 · 9 comments
Labels
A-ast Area - AST A-parser Area - Parser C-bug Category - Bug

Comments

@DonIsaac
Copy link
Contributor

DonIsaac commented Aug 7, 2024

Spans for binding identifiers cover the identifier itself and the type annotation on BindingPattern. This was introduced in #2624, and is blocking oxc-project/backlog#84.

Expected Behavior

//    ___________ BindingPattern
const foo: number = 1
//    ^^^-------- TSTypeAnnotation
//    ^^^BindingIdentifier

Actual Behavior

//    ___________ BindingPattern and BindingIdentifier
const foo: number = 1
//       -------- TSTypeAnnotation
@DonIsaac DonIsaac added C-bug Category - Bug A-parser Area - Parser A-ast Area - AST labels Aug 7, 2024
DonIsaac added a commit that referenced this issue Aug 7, 2024
Workaround until #4739 is fixed. This is not performant.
@Boshen
Copy link
Member

Boshen commented Aug 8, 2024

This is a weird one ...

image

@Dunqing
Copy link
Member

Dunqing commented Aug 8, 2024

This is also weird in TypeScript, Why not { pos: 6, end: 7 }?
image

@Boshen
Copy link
Member

Boshen commented Aug 8, 2024

Shall we go for the more intuitive version? i.e.

//    ___________ BindingPattern
const foo: number = 1
//       ^^^^^^^^ TSTypeAnnotation
//    ^^^BindingIdentifier

@DonIsaac
Copy link
Contributor Author

DonIsaac commented Aug 8, 2024

The cause is BindingPattern gets its span from kind, which covers the whole BindingPattern. I see two possible solutions:

  1. give BindingPattern its own span field, which would increase its size to 40 bytes in total
  2. derive span as kind.span().merge(type_annotation.span), which would save size at the cost of a) GetSpan performance, b) we could not implement GetSpanMut, and c) we'd need to create a custom GetSpan impl for BindingPattern.

@Boshen
Copy link
Member

Boshen commented Aug 8, 2024

Ahh, I don't like either of these options. What if we accept that it's the way it is and hack it in no-unused-vars alone?

@DonIsaac
Copy link
Contributor Author

DonIsaac commented Aug 8, 2024

That's also acceptable assuming nobody else needs this.

@magic-akari
Copy link
Contributor

This is weird in SWC.

image image

swc-project/swc#8856

DonIsaac added a commit that referenced this issue Aug 8, 2024
Workaround until #4739 is fixed. This is not performant.
@overlookmotel
Copy link
Contributor

In my opinion, we should add a span field to BindingPattern.

Yes, that costs 8 bytes. But having a weird oddity like this is a footgun, and is bound to lead to trouble. I ran into it on #4473 and couldn't understand what was going on, and Don's also run into it here. That's 2 people tripped up by this in 2 weeks!

If we wanted to, later on we could potentially optimize that field out again by making span access via a span() method only. But we could also do the same thing to various other structs (e.g. BinaryExpression), so I don't think it makes a great deal of sense to only do it on BindingPattern.

@Boshen
Copy link
Member

Boshen commented Aug 23, 2024

Close as won't fix. Let's try keep this as is and hack no-unused-vars.

@Boshen Boshen closed this as not planned Won't fix, can't repro, duplicate, stale Aug 23, 2024
Boshen pushed a commit that referenced this issue Sep 25, 2024
# What This PR Does

Enhance's `oxc_semantic`'s integration tests with a regression test suite that ensures semantic's contract guarantees hold over all test cases in typescript-eslint's scope snapshot tests. Each test case checks a separate assumption and runs independently from other test cases.

This PR sets up the code infrastructure for this test suite and adds two test cases to start us off:
1. Reflexivity tests for `IdentifierReference` and `Reference`
2. Symbol declaration reflexivity tests between declarations in `SymbolTable` and their corresponding node in the AST.

Please refer to the doc comments for each of these tests for an in-depth explanation.

## Aren't our existing tests sufficient?
`oxc_semantic` is currently tested directly via
1. scope snapshot tests, ported from `typescript-eslint`
2. Hand-written tests using `SemanticTester` in `tests/integration`

And indirectly via

3. Conformance test suite over Test262/TypeScript/Babel
4. Linter snapshot tests

Shouldn't this be sufficient? I argue not, for two reasons:

## 1. Clarify Contract Ambiguity

When using `Semantic`, I often find myself asking these questions?
* Does `semantic.symbols().get_declaration(id)` point to a `BindingIdentifer`/`BindingPattern` or the declaration that holds an identifier/pattern?
* Will a `Reference`'s `node_id` point me to an `IdentifierReference` or the expression/statement that is holding an `IdentifierReference`?
* When will `BindingIdentifier`'s `symbol_id` get populated? can we guarantee that after semantic analysis it will never be `None`?
* What actually _is_ the node covered by `semantic.symbols().get_span(id)`? This one really messed me up, and resulted in me creating #4739.
* What scope does `Function::scope_id` point to? The one where the function is declared? The one created by its body? The one created by the type annotations but before the function body? Or something else entirely?

**These test cases are meant to answer such questions and guarnatee those answers as test cases**. No other existing testing solution currently upholds such promises: they only tell us if code expecting one answer or another produces an unexpected result. However, those parts of the codebase could always be adjusted to conform to new `Semantic` behavior, meaning no contract guarantees are actually upheld.

## 2. Existing Tests Do Not Test The Same Behavior

I'll cover each above listed test case one-by-one:

1. For starters, these tests only cover scopes. Additionally, they only tell us **how behavior has changed**, not that **behavior is now incorrect**.
2. These _do_ generally cover the same behaviors, but **are not comprehensive and are difficult to maintain**. These are unit tests that should be used hand-in-hand with this new test suite.
3. The most relevant tests here are for the parser. However, these tests **only tell us if a syntax/parse error was produced**, and tell us nothing about the validity of `Semantic`.
4. Relying on lint rule's output is a a mediiocre proxy of `Semantic`'s behavior at best. They can tell us if changes to `Semantic` break assumptions made by lint rules, but they do not tell us if **those assumptions are the ones we want to uphold to external crates consuming `Semantic`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ast Area - AST A-parser Area - Parser C-bug Category - Bug
Projects
None yet
Development

No branches or pull requests

5 participants