-
Notifications
You must be signed in to change notification settings - Fork 14
Parse Private Names #4
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
Conversation
src/compiler/factory.ts
Outdated
@@ -138,6 +138,10 @@ namespace ts { | |||
: node; | |||
} | |||
|
|||
export function updatePrivateName(node: PrivateName): PrivateName { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this function? It doesn't seem to be making a modified PrivateName
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix
src/compiler/visitor.ts
Outdated
@@ -221,6 +221,8 @@ namespace ts { | |||
|
|||
case SyntaxKind.Identifier: | |||
return updateIdentifier(<Identifier>node, nodesVisitor((<Identifier>node).typeArguments, visitor, isTypeNodeOrTypeParameterDeclaration)); | |||
case SyntaxKind.PrivateName: | |||
return updatePrivateName(node as PrivateName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no modification is needed here, we can probably remove this case, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix, good catch
@@ -1386,6 +1389,17 @@ namespace ts { | |||
return finishNode(node); | |||
} | |||
|
|||
function createPrivateName(): PrivateName { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just call this parsePrivateName
and get rid of the function below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
following the pattern set by other functions such as createIdentifier()
and parseIdentifier()
0fda1ac
to
b385921
Compare
@@ -741,6 +742,13 @@ namespace ts { | |||
expression: Expression; | |||
} | |||
|
|||
export interface PrivateName extends Declaration { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should extend Node
not Declaration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @RyanCavanaugh ! I'll check this out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RyanCavanaugh in binder.ts
, I think I need to be able to pass instances of PrivateName
as the node
parameter to addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags)
. In order to do this, doesn't PrivateName
have to be an identifier?
It's likely I'm missing what a Declaration
is and why Identifier
extends Declaration
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I was mistaken on this one. extends Declaration
is fine
@@ -1176,7 +1184,7 @@ namespace ts { | |||
|
|||
export interface StringLiteral extends LiteralExpression { | |||
kind: SyntaxKind.StringLiteral; | |||
/* @internal */ textSourceNode?: Identifier | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms). | |||
/* @internal */ textSourceNode?: Identifier | PrivateName | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this case should occur (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RyanCavanaugh thanks for noticing this, I wasn't sure what to do here.
Without the change above (allowing textSourceNode
values to be PrivateName
s) the factory function for creating literals from nodes doesn't compile:
https://github.com/bloomberg/TypeScript/pull/4/files#diff-9c914fcc05c72a72db62c0a023b15f3fR110
should createLiteralFromNode
work for PropertyNameLiteral
s that are private names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @RyanCavanaugh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createLiteralFromNode
shouldn't accept a PrivateName
node - unlike property names (whose names are meaningful in isolation), private names can't be used anywhere a literal is needed.
Any place where createLiteral
is being called today with a node that is sourced from a property name, we need to add logic to do the right thing for PrivateName
s. This should just be mostly assert
s to make sure we don't e.g. try to rewrite x.#foo
with x["#foo"]
(something we currently treat as being always OK).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I incorporated this feedback and disallowed creating literals from private names.
I think the code does what we want now, with these caveats:
- added this type alias, but it helped avoid making big changes to the type hierarchy in types.d.ts.
- small new TODO in the transformer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @RyanCavanaugh would you be able to take another look?
src/compiler/utilities.ts
Outdated
@@ -2641,8 +2653,8 @@ namespace ts { | |||
return node.kind === SyntaxKind.Identifier && (<Identifier>node).escapedText === "Symbol"; | |||
} | |||
|
|||
export function isPushOrUnshiftIdentifier(node: Identifier) { | |||
return node.escapedText === "push" || node.escapedText === "unshift"; | |||
export function isPushOrUnshiftIdentifier(node: Identifier | PrivateName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be legal to invoke this with a PrivateName
src/compiler/utilities.ts
Outdated
@@ -770,12 +771,21 @@ namespace ts { | |||
|
|||
export function entityNameToString(name: EntityNameOrEntityNameExpression): string { | |||
switch (name.kind) { | |||
// fall through |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to be have left in unintentionally
Possible bug (haven't tried it but there should be a testcase): class foo {
[k: string]: any;
b() {
// Should error
this.#x = 10;
}
} |
@RyanCavanaugh you were right that this doesn't error (but should): class foo {
[k: string]: any;
b() {
// Should error
this.#x = 10;
}
}
|
@mheiber you'd probably just want to add a baseline test, which doesn't directly encode any "this should happen" information, so I think it would make sense to just add the testcase and then let the next PR update its baseline outputs |
As for behavior, in const prop = getPropertyOfType(apparentType, right.escapedText);
if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) {
markAliasReferenced(parentSymbol, node);
}
if (!prop) {
HERE ^^^^^^^^^^^^
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
if (!(indexInfo && indexInfo.type)) {
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType);
}
return errorType;
}
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}
propType = indexInfo.type;
} Here, we get The diff should be approximately as simple as if (!prop && /* the property name isn't a private name */) {
+++++++++++++++++++++++++++++++++++++++++++++++ |
8051b35
to
84bc7e9
Compare
@@ -535,7 +535,8 @@ namespace ts { | |||
const propertyNames: Expression[] = []; | |||
let computedTempVariableOffset = 0; | |||
for (let i = 0; i < elements.length - 1; i++) { | |||
const propertyName = getPropertyNameOfBindingOrAssignmentElement(elements[i]); | |||
// can safely assume property name is not a PrivateName because PrivateNames are nto allowed in object literals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo, "PrivateNames are not allowed in object literals"
src/compiler/utilities.ts
Outdated
@@ -2642,7 +2653,7 @@ namespace ts { | |||
} | |||
|
|||
export function isPushOrUnshiftIdentifier(node: Identifier) { | |||
return node.escapedText === "push" || node.escapedText === "unshift"; | |||
return isIdentifier(node) && node.escapedText === "push" || node.escapedText === "unshift"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't isIdentifier(node)
always true
here? If not, this expression is not correct. It should be isIdentifier(node) && (node.escapedText === "push" || node.escapedText === "unshift)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch. True, the node will always be an identifier, fixed
7a27449
to
684c955
Compare
/** | ||
* accessor declaration that can be converted to an expression (`name` field cannot be a `PrivateName`) | ||
*/ | ||
type ExpressionableAccessorDeclaration = AccessorDeclaration & {name: Exclude<PropertyName, PrivateName>}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be more consistent to define like this:
interface ExpressionableAccessorDeclaration extends AccessorDeclaration {
name: Exclude<PropertyName, PrivateName>;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yours is easier on the eyes, for sure. But since AccessorDeclaration is a type it seems more consistent to me for ExpressionableAccessorDeclaration to be a type.
065af59
to
1ad69a0
Compare
f754933
to
a3a4098
Compare
and check that private names not used in parameters Signed-off-by: Max Heiber <max.heiber@gmail.com>
@@ -266,6 +266,9 @@ namespace ts { | |||
Debug.assert(isWellKnownSymbolSyntactically(nameExpression)); | |||
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name)); | |||
} | |||
if (isPrivateName(node)) { | |||
return nodePosToString(node) as __String; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ignore this: it's a mistake and to be removed in #5. Actually has no effect, since there is no way the node
can be a private name here anyway.
@@ -21876,6 +21878,9 @@ namespace ts { | |||
checkGrammarDecoratorsAndModifiers(node); | |||
|
|||
checkVariableLikeDeclaration(node); | |||
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) { | |||
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we plan to undo this change is in #5
@@ -29288,6 +29293,10 @@ namespace ts { | |||
checkESModuleMarker(node.name); | |||
} | |||
|
|||
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) { | |||
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we plan to undo this change in #5
@@ -535,7 +535,8 @@ namespace ts { | |||
const propertyNames: Expression[] = []; | |||
let computedTempVariableOffset = 0; | |||
for (let i = 0; i < elements.length - 1; i++) { | |||
const propertyName = getPropertyNameOfBindingOrAssignmentElement(elements[i]); | |||
// can safely assume property name is not a PrivateName because PrivateNames are not allowed in object literals | |||
const propertyName = getPropertyNameOfBindingOrAssignmentElement(elements[i]) as Exclude<PropertyName, PrivateName>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should I add a Debug.assert here? Don't want this to be a bug vector if private names are eventually allowed in object literals
Note that these annotations are parsed by the tool in Microsoft/vscode-gdpr-tooling; the associated PR #4 adds Typescript to what the tool processes.
* Add baselines * Update baselines Signed-off-by: Joseph Watts <jwatts43@bloomberg.net>
* wip: add types * wip * Add cases * Add some case * Add more check * accept baseline * add abstract abd declare method * add override in declaration * accept baseline * add property override * Fix decalre modifier * update baseline * Add more cases * make lint happy * make lint happy * Update description * Add codefix * simplify code * accept baseline * Update desc * Accept baseline * Add override completions * filter out implements field in override context * fix tests * Add parameter property check * Accept baseline * acept baseline * Add parameter property to declaration code action * Add quickfix for override parameter property * fix code style * Add override with interface tests * Add more cases about modifier position * rename flag * rename flags * Added tests. * Accepted baselines. * Always issue errors for unnecessary 'override' modifiers. * Accepted baselines. * Override perf (#4) * try cache check result * pre check for override * Do not issue error if implement abstract * Add abstract-spec check * Avoid override dead lock * Add more case * Add codefix for new error * Fix error message * Add jsdoc override tag (#6) * Override jsdoc tag (#7) * accept baseline * Disallow codefix in js * update baseline * Omit override in d.ts * Add more case in js * Accept baseline * fix override js test * fix crlf * Revert merge conflict changes * Accept baseline * Avoid space * Fix CR issues * Accept baseline * Fix typo and add more check * Fix error name Co-authored-by: Daniel Rosenwasser <Daniel.Rosenwasser@microsoft.com>
Description of Changes
Parser and Scanner:
¬
(hook) instead of#
(hash) to test parser error recovery.Steps to Test
npm run build:compiler
)privateNames
directory carefully, the rest are minor tweaks that I hope don't matter.node -e "ts = require('./built/local/tsserver');visit = node => {console.log(node); ts.forEachChild(node, visit)};visit(ts.createSourceFile('f', fs.readFileSync('../tssample/src/1.ts', 'utf8')))