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

PrivateName does not permit escape characters #50851

Closed
DanielRosenwasser opened this issue Sep 19, 2022 · 7 comments · Fixed by #50856
Closed

PrivateName does not permit escape characters #50851

DanielRosenwasser opened this issue Sep 19, 2022 · 7 comments · Fixed by #50856
Assignees
Labels
Bug A bug in TypeScript Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Fix Available A PR has been opened for this issue

Comments

@DanielRosenwasser
Copy link
Member

Discovered by @amcasey while running TS on test262.

export class IdentifierNameWithEscape {
    \u{78}: number;

    constructor() {
        this.\u{78} = 0;
    }

    doThing() {
        this.x = 42;
    }
}

export class PrivateIdentifierWithEscape {
    #\u{78}: number;

    constructor() {
        this.#\u{78} = 0;
    }

    doThing() {
        this.#x = 42;
    }
}

Currently IdentifierNameWithEscape parses and type-checks correctly; however, PrivateIdentifierWithEscape does not even parse.

@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Help Wanted You can do this Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". labels Sep 19, 2022
@DanielRosenwasser DanielRosenwasser added this to the Backlog milestone Sep 19, 2022
@DanielRosenwasser
Copy link
Member Author

Another thing to check

class IdentifierNameWithEscape {
    x\u{78}: number;

    constructor() {
        this.x\u{78} = 0;
    }

    doThing() {
        this.xx = 42;
    }
}

class PrivateIdentifierWithEscape {
    #x\u{78}: number;

    constructor() {
        this.#x\u{78} = 0;
    }

    doThing() {
        this.#xx = 42;
    }
}

The first case works perfectly. The second case correctly resolves this.#x\u{78}, but not this.#xx even though they're really the same.

@DanielRosenwasser DanielRosenwasser self-assigned this Sep 19, 2022
@DanielRosenwasser DanielRosenwasser removed the Help Wanted You can do this label Sep 19, 2022
@DanielRosenwasser
Copy link
Member Author

DanielRosenwasser commented Sep 20, 2022

Here are the problems to be solved:

  1. The scanner doesn't know that a \ can follow a #. That is sort of easy to fix.
  2. The parser asks for the literal text of the token in source, as opposed to the "cooked" character values. This is wrong, and means that unless you wrote you private identifiers the same way throughout, they won't ever be looked up to the same symbol. Fixing that naively, for some reason, seems to cause a lot of test issues and I'm not sure why.
  3. An extended escape is an ES2015-specific feature, and for some reason we make the scanner act differently instead of parsing and issuing a span. Fixing the behavior is awkward because it may differ between ES5 and ES2015+.

@fatcerberus
Copy link

Why are these escapes even a thing? They're like a modernized version of C/C++ trigraphs...

@DanielRosenwasser
Copy link
Member Author

I'm going to guess that auto-generated code was the use-case, and then extended escape sequences were added for parity.

@Jamesernator
Copy link

I'm going to guess that auto-generated code was the use-case, and then extended escape sequences were added for parity.

I believe the main reason is for encodings where unicode isn't available (e.g. ASCII), i.e. if your system only supports ASCII then you can transform any JS source by replacing characters outside of the ASCII range by their escapes.

e.g. If you have:

const π = 3.14;
const constants = { π: 3.14 };
const s = "π";

Then replacing all non-ASCII characters with their unicode escapes works in any syntatic position:

const \u{03C0} = 3.14;
const constants = { \u{03C0}: 3.14 };
const s = "\u{03C0}";

Your conversion doesn't need to be AST aware, because the same escapes that work in string literals also work for identifiers or such, so a simple find and replace will work.

@fatcerberus
Copy link

Okay, allowing a dumb find-and-replace to be fully lossless does make sense. Handling Unicode <-> ASCII is hard enough as it is, so it’s nice that someone wanted to remove at least some of the barriers.

const π = 3.14;

I don’t think I would ever let this pass code review; someone thinks they’re Clever Dan 😄

@DanielRosenwasser
Copy link
Member Author

I'm prototyping ES3-ES5 extended escape support in #50918.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants