Skip to content

fix(isolated-declarations): preserve variable declarations when type with same name is referenced in implements clause#16328

Merged
Dunqing merged 5 commits intomainfrom
copilot/fix-variable-shadowing-issue
Dec 1, 2025
Merged

fix(isolated-declarations): preserve variable declarations when type with same name is referenced in implements clause#16328
Dunqing merged 5 commits intomainfrom
copilot/fix-variable-shadowing-issue

Conversation

Copy link
Contributor

Copilot AI commented Dec 1, 2025

  • Understand the issue: variable declarations with shadowed type names are dropped when the type is referenced in implements clause
  • Identify root cause: add_reference and resolve_references were overwriting flags instead of combining them
  • Implement fix: use bitwise OR to combine KindFlags when adding references and merging scopes
  • Add test case to fixtures (extended shadowed.ts with issue perf(span): faster conversion of path/extension to SourceType #10996 test case)
  • Run tests and update snapshots
  • Run code review
  • Run CodeQL security check (no issues found)
  • Verify fix manually with original issue example
  • Reverted .gitignore change per reviewer feedback

Summary

This PR fixes issue #10996 where variable declarations with shadowed type names were incorrectly dropped when the type was referenced in a class implements clause.

Problem

When a variable name is shadowed by a type alias and that type is referenced in implements, the variable declaration was missing from .d.ts output.

Root Cause

ScopeTree used HashMap::insert() which overwrites reference flags. When a name had both KindFlags::Value (from typeof Test2) and KindFlags::Type (from implements Thing<Test2>), only the last one was preserved.

Fix

Changed add_reference() and resolve_references() to combine flags using bitwise OR (|=) instead of overwriting.

Original prompt

This section details on the original issue you should resolve

<issue_title>[isolatedDeclarations] variable emit is dropped if the variable is shadowed as a type name and the type is part of a class implements interface</issue_title>
<issue_description># Repo

export interface Thing<T> {}

/** WORKS */

const Test1 = 'test1' as const;
export type Test1 = typeof Test1;
export class Class1 {
  readonly id: 'test1' = Test1;
}

/** DOES NOT WORK */

const Test2 = 'test2' as const;
export type Test2 = typeof Test2;
export class Class2 implements Thing<Test2> {
  readonly id: 'test2' = Test2;
}

Expected

export interface Thing<T> {
}
/** WORKS */
declare const Test1: "test1";
export type Test1 = typeof Test1;
export declare class Class1 {
    readonly id: 'test1';
}
/** DOES NOT WORK */
declare const Test2: "test2";
export type Test2 = typeof Test2;
export declare class Class2 implements Thing<Test2> {
    readonly id: 'test2';
}
export {};

ts playground

Actual

export interface Thing<T> {}
/** WORKS */
declare const Test1: "test1";
export type Test1 = typeof Test1;
export declare class Class1 {
	readonly id: "test1";
}
export type Test2 = typeof Test2;
export declare class Class2 implements Thing<Test2> {
	readonly id: "test2";
}
export {};

oxc playground

Note the missing emit of declare const Test2: "test2";</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@graphite-app
Copy link
Contributor

graphite-app bot commented Dec 1, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copilot AI and others added 3 commits December 1, 2025 08:44
…iting

Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
…mplements clause

Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix variable emit dropping due to shadowed type name fix(isolated-declarations): preserve variable declarations when type with same name is referenced in implements clause Dec 1, 2025
Copilot AI requested a review from Dunqing December 1, 2025 08:55
@Dunqing Dunqing marked this pull request as ready for review December 1, 2025 13:09
Copilot AI review requested due to automatic review settings December 1, 2025 13:10
Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical bug in the isolated declarations transformer where variable declarations were incorrectly dropped from .d.ts output. The issue occurred when a variable was shadowed by a type alias with the same name, and that type alias was referenced in a class implements clause.

Key Changes:

  • Fixed add_reference() to merge KindFlags with bitwise OR instead of overwriting
  • Fixed resolve_references() to merge flags when propagating unresolved references to parent scopes
  • Added comprehensive test case demonstrating the fix

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
crates/oxc_isolated_declarations/src/scope.rs Fixed add_reference() and resolve_references() to merge KindFlags using bitwise OR (|=) instead of overwriting with HashMap::insert()
crates/oxc_isolated_declarations/tests/fixtures/shadowed.ts Added test case for variable shadowed by type alias referenced in implements clause
crates/oxc_isolated_declarations/tests/snapshots/shadowed.snap Updated snapshot showing correct emission of both const and type declarations
.gitignore Added .serena/memories to ignored paths (unrelated maintenance change)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions github-actions bot added A-isolated-declarations Isolated Declarations C-bug Category - Bug labels Dec 1, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Dec 1, 2025

CodSpeed Performance Report

Merging #16328 will not alter performance

Comparing copilot/fix-variable-shadowing-issue (2a21e6c) with main (5ad5435)1

Summary

✅ 38 untouched
⏩ 7 skipped2

Footnotes

  1. No successful run was found on main (55cf157) during the generation of this report, so 5ad5435 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

  2. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Dunqing Dunqing merged commit f564687 into main Dec 1, 2025
34 of 47 checks passed
@Dunqing Dunqing deleted the copilot/fix-variable-shadowing-issue branch December 1, 2025 13:29
taearls pushed a commit to taearls/oxc that referenced this pull request Dec 11, 2025
…with same name is referenced in implements clause (oxc-project#16328)

- [x] Understand the issue: variable declarations with shadowed type
names are dropped when the type is referenced in implements clause
- [x] Identify root cause: `add_reference` and `resolve_references` were
overwriting flags instead of combining them
- [x] Implement fix: use bitwise OR to combine `KindFlags` when adding
references and merging scopes
- [x] Add test case to fixtures (extended shadowed.ts with issue oxc-project#10996
test case)
- [x] Run tests and update snapshots
- [x] Run code review 
- [x] Run CodeQL security check (no issues found)
- [x] Verify fix manually with original issue example
- [x] Reverted .gitignore change per reviewer feedback

## Summary

This PR fixes issue oxc-project#10996 where variable declarations with shadowed
type names were incorrectly dropped when the type was referenced in a
class `implements` clause.

### Problem
When a variable name is shadowed by a type alias and that type is
referenced in `implements`, the variable declaration was missing from
`.d.ts` output.

### Root Cause
`ScopeTree` used `HashMap::insert()` which overwrites reference flags.
When a name had both `KindFlags::Value` (from `typeof Test2`) and
`KindFlags::Type` (from `implements Thing<Test2>`), only the last one
was preserved.

### Fix
Changed `add_reference()` and `resolve_references()` to combine flags
using bitwise OR (`|=`) instead of overwriting.

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[isolatedDeclarations] variable emit is dropped if the
variable is shadowed as a type name and the type is part of a class
implements interface</issue_title>
> <issue_description># Repo
> 
> ```ts
> export interface Thing<T> {}
> 
> /** WORKS */
> 
> const Test1 = 'test1' as const;
> export type Test1 = typeof Test1;
> export class Class1 {
>   readonly id: 'test1' = Test1;
> }
> 
> /** DOES NOT WORK */
> 
> const Test2 = 'test2' as const;
> export type Test2 = typeof Test2;
> export class Class2 implements Thing<Test2> {
>   readonly id: 'test2' = Test2;
> }
> ```
> 
> # Expected
> 
> ```ts
> export interface Thing<T> {
> }
> /** WORKS */
> declare const Test1: "test1";
> export type Test1 = typeof Test1;
> export declare class Class1 {
>     readonly id: 'test1';
> }
> /** DOES NOT WORK */
> declare const Test2: "test2";
> export type Test2 = typeof Test2;
> export declare class Class2 implements Thing<Test2> {
>     readonly id: 'test2';
> }
> export {};
> ```
> [ts
playground](https://www.typescriptlang.org/play/?#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDgFQAtkBzAHnwD44BvAXwCgGB6AKlbgHUB5AJQGkAynFbMm2CEgDO8fMBkBGOAF44AclSK1cTFLgTpMANxxmzODGJ6SwGHuABbBDFQATBqEiwLATzB45RRVffwh0AnkYBSMPcGh4bAAbXT0AYWSpKSUaBjg4KGBMV0lEn0RXAC51TSjtVUComMYWdjgAEW4AUWEAOW58Lj5+ETEGAxkImQAmYI1Iqe1dfUkZEzM4Yvk4JAh4G3hHZzdYr3gYPwD54PPQ8IapmM94-Qy015mEBzBEx2AUPSIpAo82oOTyBSKJTKCEq1XmdUmMAeDGaQA)
> 
> # Actual
> 
> ```ts
> export interface Thing<T> {}
> /** WORKS */
> declare const Test1: "test1";
> export type Test1 = typeof Test1;
> export declare class Class1 {
> 	readonly id: "test1";
> }
> export type Test2 = typeof Test2;
> export declare class Class2 implements Thing<Test2> {
> 	readonly id: "test2";
> }
> export {};
> ```
> [oxc
playground](https://playground.oxc.rs/#eNp1VE1PGzEQ/SuWL5UQVSBSJRRKpQqIVJUSSqJyycXZnU0MXntrzxKiKP+9z5u1idr0ktjz+ebN825lIUeS3hrnWWjL5CtVkJittF1+nn0R293czu3g5EQ8TR6/T8XJIN4LZwOLGQU+F1fiA8fDB6GC6ByXYjAQvNJBLImDoFozUzm3fRfeNGjQ58aLq/bXyxxSGBWCuI6/52I7t0J4UqWzZiN0OcoNr3JeRnkzuZ2K+8msg/sP2mFCO/wLbekoCOs4Iv4/4Jh+AHh4FPBQ6LoxVJPF7D2PMRhcHh8EWPaDxHrdIPJUOjnaSt/a+GewFzli39KprJyvFdB5OaqUCbCwVzZEc7bo4IzCADcEWF6xxpgpf73S6Nlgxzm8VnZp3q+FqxtPARm9IRSugXufHzb1wpl0K6plH7Y7lY3yIeLaQk5MNqAtpMXhDeMoY9z6kbj1dtJy0CWNW1tEZKlU7En+lR6UR26ydnm/Lr5Z9hoVi+wIBNisi1vvne+twBCp6jDgfEDVVraBZmrxPhSrxZMueSVHw1NJtpxUd9piSmkqwG3QjXv/xRm6YYuGfraO33l6Dm/TI+bf8fbgXYNeUoWPlqikEjWxJw14y2tX1yr6jIEVc+g8LGZZp/nhX6tNQMjCq+KFeIqlIT0FJ6uqaY+87+8Wz1Twk1cNaiRSY6MO61cGkYsWCMkfpIGtLKM9X6w8nkL8NITh2fknFACDN1QhZ+x8J/SxJlPmheAhkNdR9cpAeA66y4uBF08qW38Qq1IxSOi3dlywWxkAtsHuyVsFzSWohStpSd3TsHHHWY7PoXT4nPVsWrznrlayGFqm2K5K0nkUS3oEmNw1d/RKuegLUXMPlpN2ulTo0ZkxtBkzXskvXDjgUtu4g+jKpx3MZUdfNOfTLtojzWmm3R88D/Yg)
> 
> Note the missing emit of `declare const Test2:
"test2";`</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>

- Fixes oxc-project#16314

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/oxc-project/oxc/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-isolated-declarations Isolated Declarations C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[isolatedDeclarations] variable emit is dropped if the variable is shadowed as a type name and the type is part of a class implements interface

3 participants