Error on accessing private property through destructuring assignment, and mark property used (#26381)

* Error on accessing private property through destructuring assignment, and mark property used

* Factor out getTypeOfObjectLiteralDestructuringProperty
This commit is contained in:
Andy
2018-08-13 14:08:00 -07:00
committed by GitHub
parent ec1cc0ae57
commit e8b72aa7d9
11 changed files with 316 additions and 39 deletions

View File

@@ -17736,17 +17736,15 @@ namespace ts {
* Check whether the requested property access is valid.
* Returns true if node is a valid property access, and false otherwise.
* @param node The node to be checked.
* @param left The left hand side of the property access (e.g.: the super in `super.foo`).
* @param type The type of left.
* @param prop The symbol for the right hand side of the property access.
* @param isSuper True if the access is from `super.`.
* @param type The type of the object whose property is being accessed. (Not the type of the property.)
* @param prop The symbol for the property being accessed.
*/
function checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration | ImportTypeNode, left: Expression | QualifiedName | ImportTypeNode, type: Type, prop: Symbol): boolean {
function checkPropertyAccessibility(
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
isSuper: boolean, type: Type, prop: Symbol): boolean {
const flags = getDeclarationModifierFlagsFromSymbol(prop);
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ?
node.name :
node.kind === SyntaxKind.ImportType ?
node :
(<QualifiedName>node).right;
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : node.kind === SyntaxKind.ImportType ? node : node.name;
if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) {
// Synthetic property with private constituent property
@@ -17754,7 +17752,7 @@ namespace ts {
return false;
}
if (left.kind === SyntaxKind.SuperKeyword) {
if (isSuper) {
// TS 1.0 spec (April 2014): 4.8.2
// - In a constructor, instance member function, instance member accessor, or
// instance member variable initializer where this references a derived class instance,
@@ -17807,7 +17805,7 @@ namespace ts {
// Property is known to be protected at this point
// All protected properties of a supertype are accessible in a super access
if (left.kind === SyntaxKind.SuperKeyword) {
if (isSuper) {
return true;
}
@@ -17940,7 +17938,7 @@ namespace ts {
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left, apparentType, prop);
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop);
if (assignmentKind) {
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
@@ -18174,16 +18172,16 @@ namespace ts {
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: __String): boolean {
switch (node.kind) {
case SyntaxKind.PropertyAccessExpression:
return isValidPropertyAccessWithType(node, node.expression, propertyName, getWidenedType(checkExpression(node.expression)));
return isValidPropertyAccessWithType(node, node.expression.kind === SyntaxKind.SuperKeyword, propertyName, getWidenedType(checkExpression(node.expression)));
case SyntaxKind.QualifiedName:
return isValidPropertyAccessWithType(node, node.left, propertyName, getWidenedType(checkExpression(node.left)));
return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getWidenedType(checkExpression(node.left)));
case SyntaxKind.ImportType:
return isValidPropertyAccessWithType(node, node, propertyName, getTypeFromTypeNode(node));
return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getTypeFromTypeNode(node));
}
}
function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean {
return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.ImportType ? node : node.expression, property.escapedName, type)
return isValidPropertyAccessWithType(node, node.kind !== SyntaxKind.ImportType && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type)
&& (!(property.flags & SymbolFlags.Method) || isValidMethodAccess(property, type));
}
function isValidMethodAccess(method: Symbol, actualThisType: Type): boolean {
@@ -18206,7 +18204,7 @@ namespace ts {
function isValidPropertyAccessWithType(
node: PropertyAccessExpression | QualifiedName | ImportTypeNode,
left: LeftHandSideExpression | QualifiedName | ImportTypeNode,
isSuper: boolean,
propertyName: __String,
type: Type): boolean {
@@ -18214,9 +18212,9 @@ namespace ts {
return true;
}
const prop = getPropertyOfType(type, propertyName);
return prop ? checkPropertyAccessibility(node, left, type, prop)
return prop ? checkPropertyAccessibility(node, isSuper, type, prop)
// In js files properties of unions are allowed in completion
: isInJavaScriptFile(node) && (type.flags & TypeFlags.Union) !== 0 && (<UnionType>type).types.some(elementType => isValidPropertyAccessWithType(node, left, propertyName, elementType));
: isInJavaScriptFile(node) && (type.flags & TypeFlags.Union) !== 0 && (<UnionType>type).types.some(elementType => isValidPropertyAccessWithType(node, isSuper, propertyName, elementType));
}
/**
@@ -21093,19 +21091,19 @@ namespace ts {
return booleanType;
}
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type {
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type {
const properties = node.properties;
if (strictNullChecks && properties.length === 0) {
return checkNonNullType(sourceType, node);
}
for (const p of properties) {
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties);
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties, rightIsThis);
}
return sourceType;
}
/** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>) {
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>, rightIsThis = false) {
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
const name = property.name;
if (name.kind === SyntaxKind.ComputedPropertyName) {
@@ -21115,20 +21113,10 @@ namespace ts {
return undefined;
}
const text = getTextOfPropertyName(name);
const type = isTypeAny(objectLiteralType)
? objectLiteralType
: getTypeOfPropertyOfType(objectLiteralType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(objectLiteralType, IndexKind.Number) ||
getIndexTypeOfType(objectLiteralType, IndexKind.String);
const type = getTypeOfObjectLiteralDestructuringProperty(objectLiteralType, name, property, rightIsThis);
if (type) {
if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkDestructuringAssignment(property, type);
}
else {
// non-shorthand property assignments should always have initializers
return checkDestructuringAssignment(property.initializer, type);
}
// non-shorthand property assignments should always have initializers
return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
}
else {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(objectLiteralType), declarationNameToString(name));
@@ -21153,6 +21141,25 @@ namespace ts {
}
}
function getTypeOfObjectLiteralDestructuringProperty(objectLiteralType: Type, name: PropertyName, property: PropertyAssignment | ShorthandPropertyAssignment, rightIsThis: boolean) {
if (isTypeAny(objectLiteralType)) {
return objectLiteralType;
}
let type: Type | undefined;
const text = getTextOfPropertyName(name);
if (text) { // TODO: GH#26379
const prop = getPropertyOfType(objectLiteralType, text);
if (prop) {
markPropertyAsReferenced(prop, property, rightIsThis);
checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop);
type = getTypeOfSymbol(prop);
}
type = type || (isNumericLiteralName(text) ? getIndexTypeOfType(objectLiteralType, IndexKind.Number) : undefined);
}
return type || getIndexTypeOfType(objectLiteralType, IndexKind.String);
}
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
const elements = node.elements;
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
@@ -21214,7 +21221,7 @@ namespace ts {
return undefined;
}
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode): Type {
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode, rightIsThis?: boolean): Type {
let target: Expression;
if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) {
const prop = <ShorthandPropertyAssignment>exprOrAssignment;
@@ -21238,7 +21245,7 @@ namespace ts {
target = (<BinaryExpression>target).left;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType);
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType, rightIsThis);
}
if (target.kind === SyntaxKind.ArrayLiteralExpression) {
return checkArrayLiteralAssignment(<ArrayLiteralExpression>target, sourceType, checkMode);
@@ -21337,7 +21344,7 @@ namespace ts {
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node): Type {
const operator = operatorToken.kind;
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode);
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword);
}
let leftType: Type;
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
@@ -24400,7 +24407,7 @@ namespace ts {
const property = getPropertyOfType(parentType!, nameText)!; // TODO: GH#18217
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
if (parent.initializer && property && !isComputedPropertyName(name)) {
checkPropertyAccessibility(parent, parent.initializer, parentType!, property);
checkPropertyAccessibility(parent, parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
}
}
}