oxc-project / oxc-project/backlog

AST: Include Exported Class Decorators in Parent Spans

Open
#217 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
No language data
Stars
7
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Parent: oxc-project/backlog#210

Summary

When decorators appear before an exported class, include them in both the export declaration span and the class declaration span.

This restores the invariant that every decorator's span is contained by the decorated class and by the export node that contains that class.

Motivation

For decorators before export, the parser currently starts the export span at export and the class span at class:

@dec export class C {}
^^^^ Decorator
     ^^^^^^^^^^^^^^^^^ ExportNamedDeclaration
            ^^^^^^^^^^ Class

The Decorator is a child of Class, which is contained by ExportNamedDeclaration, but its source range begins outside both ancestors.

This breaks the usual span-containment invariant:

parent.span contains child.span

Consumers compensate with special cases. The formatter's program iterator, for example, checks for a class decorator before the export and substitutes the decorator span when ordering statements. Span-based traversal, diagnostics, source maps, and editor integrations otherwise have to account for an AST ownership tree that disagrees with the source-range tree.

Decorators are not valid on function declarations, so invalid inputs such as @dec export function f() {} should not define the intended span behavior.

Current Span Shape

@dec export class C {}
^^^^ Decorator
     ^^^^^^^^^^^^^^^^^ ExportNamedDeclaration
            ^^^^^^^^^^ Class

@dec export default class C {}
^^^^ Decorator
     ^^^^^^^^^^^^^^^^^^^^^^^^^ ExportDefaultDeclaration
                    ^^^^^^^^^^ Class

Decorators placed after the export keyword are already contained:

export @dec class C {}
^^^^^^^^^^^^^^^^^^^^^^ ExportNamedDeclaration
       ^^^^ Decorator
       ^^^^^^^^^^^^^^^ Class

Proposed Span Shape

Start both the export and class spans at the earliest decorator when decorators precede export:

@dec export class C {}
^^^^^^^^^^^^^^^^^^^^^^ ExportNamedDeclaration
^^^^ Decorator
^^^^^^^^^^^^^^^^^^^^^^ Class

@dec export default class C {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ExportDefaultDeclaration
^^^^ Decorator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Class

Conceptually:

export.span.start = min(export_keyword.start, first_decorator.span.start);
class.span.start = min(class_keyword.start, first_decorator.span.start);

End positions remain unchanged.

Syntax Mapping

Source form Export span starts at Class span starts at
export class C {} export class
@dec export class C {} @dec @dec
@dec export default class C {} @dec @dec
export @dec class C {} export @dec
export default @dec class C {} export @dec
@dec class C {} No export node @dec

Guaranteed Invariants

  • Every class decorator span is contained by its Class span.
  • A decorated class span is contained by its export declaration span.
  • The AST ownership hierarchy and source-range hierarchy agree.
  • Undecorated export and class start positions remain unchanged.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the parser code that constructs export declaration and class spans, then inspect existing decorator-related AST tests. Reproduce the span shapes for decorators before and after export, including default exports. Done means preceding decorators are included in both relevant spans while undecorated and post-export cases retain their specified starts.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.