Skip to content

Conversation

@taearls
Copy link
Contributor

@taearls taearls commented Jul 19, 2025

Part of #11490

Progress Checklist:

  • Removed SimpleAssignmentTarget from the ENUMS_WHITE_LIST
  • regenerated the AST definitions
  • addressed the compile errors in oxc_semantic, oxc_formatter, oxc_ast, and oxc_mangler
  • all unit tests passing
  • all integration tests passing
  • just ready command runs successfully
  • fix conformance test failures (left comment for review)
  • self-review PR to see if I can simplify / minimize my changes

Snapshot updates:

  • After removing SimpleAssignmentTarget, the node ids in several snapshots were updated, which is expected since the generated node ids were changed.
  • the prettier_conformance test snapshot for TS is slightly different (left a comment for feedback below)

@github-actions github-actions bot added A-linter Area - Linter A-semantic Area - Semantic A-ast Area - AST A-ast-tools Area - AST tools A-formatter Area - Formatter C-cleanup Category - technical debt or refactoring. Solution not expected to change behavior labels Jul 19, 2025
@taearls taearls changed the title refactor(linter): remove AstKind from SimpleAssignmentTarget refactor(linter): remove AstKind for SimpleAssignmentTarget Jul 19, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Jul 19, 2025

CodSpeed Instrumentation Performance Report

Merging #12401 will not alter performance

Comparing taearls:07_19_refactor_remove_ast_kind_from_simple_assignment_target (4cb9a80) with main (3cdac4c)

Summary

✅ 34 untouched benchmarks

@overlookmotel
Copy link
Member

overlookmotel commented Jul 23, 2025

I thought I saw a minute ago a diff in snapshots, a lint rule erroring on the !! in something like if (x || !!(y || z)). But now it's gone!

That looked like a pattern that the rule maybe should be catching - the !!(...) is unnecessary - so I thought you might have surfaced an unrelated bug. Can you remember what it was exactly? We might want to fix that separately.

@taearls
Copy link
Contributor Author

taearls commented Jul 23, 2025

I thought I saw a minute ago a diff in snapshots, a lint rule erroring on the !! in something like if (x || !!(y || z)). But now it's gone!

That looked like a pattern that the rule maybe should be catching - the !!(...) is unnecessary - so I thought you might have surfaced an unrelated bug. Can you remember what it was exactly? We might want to fix that separately.

@overlookmotel I think you're talking about this.

The snapshot update was reverted once I had applied the change that you suggested the other day, which was to remove the self.leave_node(kind) from visit_simple_assignment_target in builder.rs.

My understanding was that the bug from my incomplete changes had caused the reporting in the snapshot to change, but it's very possible that I could be missing something here.

@overlookmotel
Copy link
Member

@overlookmotel I think you're talking about this.

Ah ha! The diff I saw was those same lines but all in green. But, as you've explained, that was reverting a previous change. So we're all good. Sorry, my question was a complete red herring.

@taearls
Copy link
Contributor Author

taearls commented Jul 24, 2025

@overlookmotel I think you're talking about this.

Ah ha! The diff I saw was those same lines but all in green. But, as you've explained, that was reverting a previous change. So we're all good. Sorry, my question was a complete red herring.

All good, thanks for looking out! My goal has been to get the tests passing without changing any of the snapshots. Ideally this refactor should not influence the functionality for end users.

To that end, once I get everything working, I want to also take a few cycles to look for opportunities to simplify / minimize my changes. I'll likely leave comments on my PR on anything I'm not sure about.

@overlookmotel
Copy link
Member

To that end, once I get everything working, I want to also take a few cycles to look for opportunities to simplify / minimize my changes. I'll likely leave comments on my PR on anything I'm not sure about.

Thank you for your very thorough approach. It's really appreciated.

FYI: What we've noticed while making other changes to AstKind is that the test coverage in linter is often not as good as it could be. Unfortunately, it's quite easy to introduce subtle bugs, and all tests may still pass. If you have time, it's ideal to go through the changes to linter and try to deduce the logic of the code, and make sure it's not changing, rather than relying solely on the tests.

This is our bad - other parts of Oxc have very thorough test coverage, which we can lean on when making changes, but the linter is less comprehensively tested. We might want to improve that (oxc-project/backlog#170), but right now this is where we are.

However, figuring out the logic of each lint rule and trying to imagine all possible edge cases is both difficult and onerous, and tricky if you're not familiar with what the lint rules do. If you don't have time to delve that deep, please feel free to leave this until review.

Another side note: If you come across any unrelated bugs you want to fix, or edge cases that you want to add tests for, it'd be ideal to do that in separate PRs. This PR has quite a large diff, so it'd be preferable for ease of review if it's 100% refactor.

Hope this is useful background.

@taearls taearls marked this pull request as ready for review July 25, 2025 18:55
@taearls taearls requested review from Dunqing and camc314 as code owners July 25, 2025 18:55
@taearls taearls force-pushed the 07_19_refactor_remove_ast_kind_from_simple_assignment_target branch from f57afc9 to 752b155 Compare July 25, 2025 19:05
@camc314
Copy link
Contributor

camc314 commented Jul 26, 2025

note to self, we need to fix all of the ecosystem CI failures before merging

https://github.com/oxc-project/oxlint-ecosystem-ci/actions/runs/16538642569

(excluding oven-sh/bun as it's a diff issue)

@camc314 camc314 self-assigned this Jul 27, 2025
@camc314
Copy link
Contributor

camc314 commented Jul 28, 2025

Another false positive:

export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
  readonly #select = (e: ColorEvent) => {
    e.stopPropagation();
    this.#pick(e.detail);
  };

  override render() {
    return html`
      <editor-menu-button
        .contentPadding=${this.tabContentPadding}
        .button=${html`
          <editor-icon-button
            aria-label=${this.label}
            .tooltip=${this.tooltip || this.label}
          >
            ${when(
              this.isText,
              () => html`
                <edgeless-text-color-icon
                  .color=${this.colorWithoutAlpha}
                ></edgeless-text-color-icon>
              `,
              () => html`
                <edgeless-color-button
                  .color=${this.colorWithoutAlpha}
                  .hollowCircle=${this.hollowCircle}
                ></edgeless-color-button>
              `
            )}
          </editor-icon-button>
        `}
      >
        ${choose(this.tabType, [
          [
            'normal',
            () => html`
              <div data-orientation="vertical">
                <slot name="other"></slot>
                <slot name="separator"></slot>
                <edgeless-color-panel
                  role="listbox"
                  class=${ifDefined(this.colorPanelClass)}
                  .value=${this.color}
                  .theme=${this.theme}
                  .palettes=${this.palettes}
                  .hollowCircle=${this.hollowCircle}
                  .hasTransparent=${false}
                  @select=${this.#select}
                >
                  ${when(
                    this.enableCustomColor,
                    () => html`
                      <edgeless-color-custom-button
                        slot="custom"
                        style=${styleMap(this.customButtonStyle)}
                        ?active=${this.isCustomColor}
                        @click=${this.switchToCustomTab}
                      ></edgeless-color-custom-button>
                    `
                  )}
                </edgeless-color-panel>
              </div>
            `,
          ],
          [
            'custom',
            () => {
              const packed = packColorsWith(
                this.theme,
                this.color,
                this.originalColor
              );
              const type = packed.type === 'palette' ? 'normal' : packed.type;
              const modes = packed.colors.map(
                preprocessColor(window.getComputedStyle(this))
              );

              return html`
                <edgeless-color-picker
                  class="custom"
                  .pick=${this.pick}
                  .colors=${{ type, modes }}
                ></edgeless-color-picker>
              `;
            },
          ],
        ])}
      </editor-menu-button>
    `;
  }

}
  ⚠ eslint(no-unused-private-class-members): 'select' is defined but never used.
   ╭─[no_unused_private_class_members.tsx:2:12]
 1 │ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
 2 │   readonly #select = (e: ColorEvent) => {
   ·            ───────
 3 │     e.stopPropagation();
   ╰────

@camc314 camc314 force-pushed the 07_19_refactor_remove_ast_kind_from_simple_assignment_target branch from 4c58946 to 07b57fb Compare July 28, 2025 11:24
@camc314 camc314 force-pushed the 07_19_refactor_remove_ast_kind_from_simple_assignment_target branch from 07b57fb to 7e34ef3 Compare July 28, 2025 11:55
@camc314 camc314 merged commit a696227 into oxc-project:main Jul 28, 2025
24 checks passed
@taearls
Copy link
Contributor Author

taearls commented Jul 28, 2025

Another false positive:

export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
  readonly #select = (e: ColorEvent) => {
    e.stopPropagation();
    this.#pick(e.detail);
  };

  override render() {
    return html`
      <editor-menu-button
        .contentPadding=${this.tabContentPadding}
        .button=${html`
          <editor-icon-button
            aria-label=${this.label}
            .tooltip=${this.tooltip || this.label}
          >
            ${when(
              this.isText,
              () => html`
                <edgeless-text-color-icon
                  .color=${this.colorWithoutAlpha}
                ></edgeless-text-color-icon>
              `,
              () => html`
                <edgeless-color-button
                  .color=${this.colorWithoutAlpha}
                  .hollowCircle=${this.hollowCircle}
                ></edgeless-color-button>
              `
            )}
          </editor-icon-button>
        `}
      >
        ${choose(this.tabType, [
          [
            'normal',
            () => html`
              <div data-orientation="vertical">
                <slot name="other"></slot>
                <slot name="separator"></slot>
                <edgeless-color-panel
                  role="listbox"
                  class=${ifDefined(this.colorPanelClass)}
                  .value=${this.color}
                  .theme=${this.theme}
                  .palettes=${this.palettes}
                  .hollowCircle=${this.hollowCircle}
                  .hasTransparent=${false}
                  @select=${this.#select}
                >
                  ${when(
                    this.enableCustomColor,
                    () => html`
                      <edgeless-color-custom-button
                        slot="custom"
                        style=${styleMap(this.customButtonStyle)}
                        ?active=${this.isCustomColor}
                        @click=${this.switchToCustomTab}
                      ></edgeless-color-custom-button>
                    `
                  )}
                </edgeless-color-panel>
              </div>
            `,
          ],
          [
            'custom',
            () => {
              const packed = packColorsWith(
                this.theme,
                this.color,
                this.originalColor
              );
              const type = packed.type === 'palette' ? 'normal' : packed.type;
              const modes = packed.colors.map(
                preprocessColor(window.getComputedStyle(this))
              );

              return html`
                <edgeless-color-picker
                  class="custom"
                  .pick=${this.pick}
                  .colors=${{ type, modes }}
                ></edgeless-color-picker>
              `;
            },
          ],
        ])}
      </editor-menu-button>
    `;
  }

}
  ⚠ eslint(no-unused-private-class-members): 'select' is defined but never used.
   ╭─[no_unused_private_class_members.tsx:2:12]
 1 │ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
 2 │   readonly #select = (e: ColorEvent) => {
   ·            ───────
 3 │     e.stopPropagation();
   ╰────

I'm curious - were there additional test cases in the main branch that also needed to be covered here? I'm not sure I follow the changes that were made.

@camc314
Copy link
Contributor

camc314 commented Jul 28, 2025

i should hopefully have fixed them all.

we have an ecosystem CI, which i just ran in this branch to flag these additional errors. that's the link to the workflow above

camc314 added a commit that referenced this pull request Jul 29, 2025
## [1.9.0] - 2025-07-29

### 💥 BREAKING CHANGES

- 5a7e72a semantic: [**BREAKING**] `AstNodes::program` return `&Program`
not `Option<&Program>` (#12515) (overlookmotel)

### 🚀 Features

- 3489ce0 linter: Add `typescript-eslint/explicit-module-boundary-types`
(#12402) (Don Isaac)

### 🐛 Bug Fixes

- 0fd3e87 linter: Default options for `eslint/yoda` (#12540) (Sysix)
- 724776f linter: Default options for `unicorn/switch-case-braces`
(#12539) (Sysix)
- fda45ea linter/promise/prefer-await-to-callbacks: False positive for
`addEventListener` (#12537) (Copilot)
- 1a710e3 linter/array-type: Fix more false negatives (#12501) (camc314)
- 2b5bf98 linter: Consistent-function-scoping false positive with
hoisted var declarations (#12523) (camc314)
- cc19c8b vscode: Fix statusbar icon order (#12544) (Christian Fehmer)
- 209d006 linter: Parse vue lang attribute without quotes (#12517)
(Sysix)
- 85a34ce linter/array-type: False negative with arrays in generic args
(#12500) (camc314)
- 98c1fbb linter/require-await: Improve async keyword detection in
get_delete_span function (#12494) (camc314)
- 7c75dba linter/require-await: Improve span calculation for object
properties (#12490) (camc314)
- 2b261cf linter/exhaustive-deps: False positive in exhaustive deps
(#12471) (camc314)

### 🚜 Refactor

- a696227 linter: Remove AstKind for SimpleAssignmentTarget (#12401)
(Tyler Earls)
- 7af38e1 napi/oxlint: Simplify `ExternalLinterLintFileCb` type (#12572)
(overlookmotel)
- 543fd53 napi/oxlint: Rename `run` to `lintFile` (#12567)
(overlookmotel)
- 0179c86 napi/oxlint: Reverse args of `ExternalLinter::new` (#12566)
(overlookmotel)
- 491c401 linter: Remove `#[must_use]` from `LintService::with_*`
methods (#12560) (overlookmotel)
- d44b0ac linter: Remove `Runner` trait (#12559) (overlookmotel)
- bea652f linter: Add `vue` and `regex` to `BuiltinLintPlugins` (#12542)
(Sysix)
- aa9dd21 linter/no-eval: Get source type from `Semantic` (#12514)
(overlookmotel)
- 5c33fc7 diagnostics: Implement `Eq` and `Ord` for `InfoPosition`
(#12505) (overlookmotel)
- 8c8c8bc napi/oxlint: Diagnostics communicate which rule via rule
index, not rule ID (#12482) (overlookmotel)
- e2d9b4d fixer: Add Debug trait to PossibleFixes and Message structs
(#12493) (camc314)
- f0b1f0d napi/oxlint, napi/parser: Remove source length from
`RawTransferMetadata` (#12483) (overlookmotel)
- 7e4959a napi/oxlint: Rename `rules` to `ruleNames` (#12477)
(overlookmotel)
- 7a0da04 diagnostics: Remove Option wrapper from MPSC channel and
sender field (#12467) (camc314)

### 🧪 Testing

- 56468c7 linter/no-unused-private-class-members: Add more test cases
(#12569) (camc314)
- 191a164 linter/no-unused-private-class-members: Add more test cases
(#12563) (camc314)
- d31adcf linter: Improve sorting diagnostics (#12504) (overlookmotel)

Co-authored-by: camc314 <18101008+camc314@users.noreply.github.com>
graphite-app bot pushed a commit that referenced this pull request Jul 30, 2025
#12596)

The removal of `AstKind::SimpleAssignmentTarget` (#12401) has made it harder to determine if a node is being assigned to, because you can no longer check if the parent `AstNode` is `AstKind::SimpleAssignmentTarget`.

Add a method to `MemberExpressionKind` which determines whether the parent assigns to it. Covers all possible positions where a `MemberExpression` gets assigned to - `x.y = value`, `x.y++`, `[x.y] = arr` etc.

We can use this method to avoid repeating this complicated logic in many places.
@taearls taearls deleted the 07_19_refactor_remove_ast_kind_from_simple_assignment_target branch November 4, 2025 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ast Area - AST A-ast-tools Area - AST tools A-formatter Area - Formatter A-linter Area - Linter A-semantic Area - Semantic C-cleanup Category - technical debt or refactoring. Solution not expected to change behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants