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

Preserve string delimiter in type printing. #60729

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

dragomirtitian
Copy link
Contributor

Related to #60540

This PR shows what the impact of preserving string delimiters in type printing would be.

cc: @jakebailey @weswigham

if (!nodeIsSynthesized(range) || !(range.flags & NodeFlags.Synthesized) || !context.enclosingFile || context.enclosingFile !== nodeSourceFile) {
if (range.kind === SyntaxKind.StringLiteral) {
const stringLiteral = range as Node as StringLiteral;
range = factory.createStringLiteral(stringLiteral.text, !!nodeSourceFile && !isStringDoubleQuoted(stringLiteral, nodeSourceFile)) as Node as T;
Copy link
Member

Choose a reason for hiding this comment

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

This could instead use factory.createStringLiteralFromNode(stringLiteral) as that should pull the string's text from its original file.

@jakebailey
Copy link
Member

We looked at this in the design meeting; the general consensus is that this is good for declaration files, but the fact that error messages are changing quote styles seems to be wrong; we should try and make diagnostic type printing normalize to " quotes because that's what our messages assume happen.

@typescript-bot
Copy link
Collaborator

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

@@ -6040,13 +6048,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}

/**
* Prints a type, forcing the conversion of string delimiters to "
* This should usually be used for errors, since in errors printed types are inclosed in '

Choose a reason for hiding this comment

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

Suggested change
* This should usually be used for errors, since in errors printed types are inclosed in '
* This should usually be used for errors, since in errors printed types are enclosed in '

@@ -8,7 +8,7 @@ declare namespace demoNS {
>f : Symbol(f, Decl(demo.d.ts, 0, 26))
}
declare module 'demoModule' {
>'demoModule' : Symbol("demoModule", Decl(demo.d.ts, 2, 1))
>'demoModule' : Symbol('demoModule', Decl(demo.d.ts, 2, 1))
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised to see symbol baselines change; were we inconsistent with these before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Symbol printing is involved in type printing and error printing. I applied the 'preserve source code delimiters in everything except errors' to the symbol printing too. This does mean that the delimiters are now preserved in symbol baselines where they previously were not.

Were they always replaced before? Don't think so. Some error messages used symbol printing for the property names and we can see in error baselines changes that some of those kept the ' delimiter instead of having it replaced with "

Comment on lines 78 to 80
"text": "\"resolution-mode\"",
"kind": "stringLiteral"
"kind": "propertyName"
},
Copy link
Member

Choose a reason for hiding this comment

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

This is funky; I'm not sure how much this matters in practice, though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, but it was already the case that some strings would be marked as things other than text in display parts.

Before, in symbol printing, some identifiers were created that didn't actually have an identifier valid text (identifier name was a string including delimiters). These identifiers had their symbol set, and during display part emit, they were marked as whatever kind of symbol they had attached. However this did not happene for all string created during symbol printing. So we had some parts of symbol names as identifiers with symbol and invalid text, and some as string literals with no symbol.

Now, I create string literals and number literals for all symbol names which are not valid identifiers and set the symbol on those too. This does mean that some parts of the symbol name now have the symbol attached to then (where before they did not) and can have the correct kind set in display parts.

These are the changes in symbolToExpression in checker.ts and the changes to emitLiteral in emitter.ts

Comment on lines 54 to +56
~
!!! error TS2741: Property ''1'' is missing in type 'T' but required in type 'S'.
!!! related TS2728 assignmentCompatWithObjectMembersStringNumericNames.ts:5:15: ''1'' is declared here.
!!! error TS2741: Property '"1"' is missing in type 'T' but required in type 'S'.
!!! related TS2728 assignmentCompatWithObjectMembersStringNumericNames.ts:5:15: '"1"' is declared here.
Copy link
Member

@jakebailey jakebailey Dec 18, 2024

Choose a reason for hiding this comment

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

🎉

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

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

I read through all of the baselines; mostly seems correct, with some odd escaping changes, and I would like someone familiar with those displayParts to weigh in on those changes.

Comment on lines 42 to 45
>sameName5a : Symbol(sameName5a, Decl(objectLiteralGettersAndSetters.ts, 5, 3))
>'\t' : Symbol('\t', Decl(objectLiteralGettersAndSetters.ts, 5, 18), Decl(objectLiteralGettersAndSetters.ts, 5, 45))
>'\t' : Symbol('\t', Decl(objectLiteralGettersAndSetters.ts, 5, 18), Decl(objectLiteralGettersAndSetters.ts, 5, 45))
>'\t' : Symbol('t', Decl(objectLiteralGettersAndSetters.ts, 5, 18), Decl(objectLiteralGettersAndSetters.ts, 5, 45))
>'\t' : Symbol('t', Decl(objectLiteralGettersAndSetters.ts, 5, 18), Decl(objectLiteralGettersAndSetters.ts, 5, 45))
>n : Symbol(n, Decl(objectLiteralGettersAndSetters.ts, 5, 55))
Copy link
Member

Choose a reason for hiding this comment

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

Where did the \ go?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code to remove the \ dates back a long long time.

symbolName.substring(1, symbolName.length - 1).replace(/\\./g, s => s.substring(1))

I have traced it back to this commit 8bb7230

Maybe @weswigham can shed more light on it, but this seems to be there to deal with escape sequences. Since when we build the string we need the escaped version, but what we have in symbolName is the source code version of the string. I will look for a better solution.

The current version can cause issues in both quick info and declaration emit:

// index.ts
class T {
    static readonly "\t" = Symbol()
}

let x = { [T['\t']]: 1 }

//index.d.ts
declare class T {
    static readonly "\t": unique symbol;
}
declare let x: {
    [T["t"]]: number; // ❌ wrong symbol
};

Playground Link

@@ -92,7 +92,7 @@ var a: {
>"a b" : Symbol("a b", Decl(objectTypeWithStringNamedPropertyOfIllegalCharacters.ts, 30, 18))

"~!@#$%^&*()_+{}|:'<>?\/.,`": number;
>"~!@#$%^&*()_+{}|:'<>?\/.,`" : Symbol("~!@#$%^&*()_+{}|:'<>?\/.,`", Decl(objectTypeWithStringNamedPropertyOfIllegalCharacters.ts, 31, 20))
>"~!@#$%^&*()_+{}|:'<>?\/.,`" : Symbol("~!@#$%^&*()_+{}|:'<>?/.,`", Decl(objectTypeWithStringNamedPropertyOfIllegalCharacters.ts, 31, 20))
Copy link
Member

Choose a reason for hiding this comment

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

This escaping is also gone, which is odd...

Comment on lines 5 to 7
>x : Symbol(x, Decl(stringLiteralPropertyNameWithLineContinuation1.ts, 0, 3))
>'text\' : Symbol('text\
', Decl(stringLiteralPropertyNameWithLineContinuation1.ts, 0, 9))
>'text\' : Symbol('text\\\n', Decl(stringLiteralPropertyNameWithLineContinuation1.ts, 0, 9))

Copy link
Member

Choose a reason for hiding this comment

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

This is certainly an improvement

@dragomirtitian dragomirtitian marked this pull request as ready for review December 20, 2024 14:56
@typescript-bot
Copy link
Collaborator

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants