Skip to content

Commit

Permalink
Fix expression checking for symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
JsonFreeman committed Feb 7, 2015
1 parent d793658 commit 2d16474
Show file tree
Hide file tree
Showing 24 changed files with 484 additions and 44 deletions.
72 changes: 59 additions & 13 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6856,6 +6856,9 @@ module ts {
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
if (hasSomeTypeOfKind(operandType, TypeFlags.ESSymbol)) {
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_a_value_of_type_symbol, tokenToString(node.operator));

This comment has been minimized.

Copy link
@yuit

yuit Feb 10, 2015

Contributor

Should this return numberType? Shouldn't it return unknown instead

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Feb 10, 2015

Author Contributor

numberType is correct. These operators always give a number, or they throw an error. But when they give a non-abrupt result, it's always a number.

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Feb 10, 2015

Author Contributor

More to the point, we know that the user wants it to be a number.

}
return numberType;
case SyntaxKind.ExclamationToken:
return booleanType;
Expand Down Expand Up @@ -6891,6 +6894,24 @@ module ts {
return numberType;
}

// Just like isTypeOfKind below, except that it returns true if *any* constituent
// has this kind.
function hasSomeTypeOfKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
return true;
}
if (type.flags & TypeFlags.Union) {
var types = (<UnionType>type).types;
for (var i = 0; i < types.length; i++) {
if (types[i].flags & kind) {
return true;
}
}
return false;
}
return false;
}

// Return true if type has the given flags, or is a union type composed of types that all have those flags.
function isTypeOfKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
Expand Down Expand Up @@ -6938,7 +6959,7 @@ module ts {
// and the right operand to be of type Any, an object type, or a type parameter type.
// The result is always of the Boolean primitive type.
if (!isTypeOfKind(leftType, TypeFlags.Any | TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number);
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
}
if (!isTypeOfKind(rightType, TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.TypeParameter)) {
error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
Expand Down Expand Up @@ -7106,14 +7127,21 @@ module ts {
// If both operands are of the Number primitive type, the result is of the Number primitive type.
resultType = numberType;
}
else if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
// If one or both operands are of the String primitive type, the result is of the String primitive type.
resultType = stringType;
}
else if (leftType.flags & TypeFlags.Any || rightType.flags & TypeFlags.Any) {
// Otherwise, the result is of type Any.
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
resultType = anyType;
else {
if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
// If one or both operands are of the String primitive type, the result is of the String primitive type.
resultType = stringType;
}
else if (leftType.flags & TypeFlags.Any || rightType.flags & TypeFlags.Any) {
// Otherwise, the result is of type Any.
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
resultType = anyType;
}

// Symbols are not allowed at all in arithmetic expressions
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
return resultType;
}
}

if (!resultType) {
Expand All @@ -7125,14 +7153,18 @@ module ts {
checkAssignmentOperator(resultType);
}
return resultType;
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanEqualsToken:
if (!checkForDisallowedESSymbolOperand(operator)) {
return booleanType;
}
// Fall through
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
if (!isTypeAssignableTo(leftType, rightType) && !isTypeAssignableTo(rightType, leftType)) {
reportOperatorError();
}
Expand All @@ -7152,6 +7184,20 @@ module ts {
return rightType;
}

// Return type is true if there was no error, false if there was an error.
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
var offendingSymbolOperand =
hasSomeTypeOfKind(leftType, TypeFlags.ESSymbol) ? node.left :
hasSomeTypeOfKind(rightType, TypeFlags.ESSymbol) ? node.right :
undefined;
if (offendingSymbolOperand) {
error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_a_value_of_type_symbol, tokenToString(operator));
return false;
}

return true;
}

function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind {
switch (operator) {
case SyntaxKind.BarToken:
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ module ts {
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: { code: 2357, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator must be a variable, property or indexer." },
The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2358, category: DiagnosticCategory.Error, key: "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter." },
The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type: { code: 2359, category: DiagnosticCategory.Error, key: "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type." },
The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'." },
The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol: { code: 2360, category: DiagnosticCategory.Error, key: "The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'." },
The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2361, category: DiagnosticCategory.Error, key: "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter" },
The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2362, category: DiagnosticCategory.Error, key: "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: { code: 2363, category: DiagnosticCategory.Error, key: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type." },
Expand Down Expand Up @@ -305,6 +305,7 @@ module ts {
super_cannot_be_referenced_in_a_computed_property_name: { code: 2466, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in a computed property name." },
A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2467, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." },
Cannot_find_global_value_0: { code: 2468, category: DiagnosticCategory.Error, key: "Cannot find global value '{0}'." },
The_0_operator_cannot_be_applied_to_a_value_of_type_symbol: { code: 2469, category: DiagnosticCategory.Error, key: "The '{0}' operator cannot be applied to a value of type 'symbol'." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@
"category": "Error",
"code": 2359
},
"The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": {
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
"category": "Error",
"code": 2360
},
Expand Down Expand Up @@ -1213,6 +1213,10 @@
"category": "Error",
"code": 2468
},
"The '{0}' operator cannot be applied to a value of type 'symbol'.": {

This comment has been minimized.

Copy link
@yuit

yuit Feb 10, 2015

Contributor

I don't think you need a value of

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Feb 10, 2015

Author Contributor

You're right, the other operator messages don't seem to say that

"category": "Error",
"code": 2469
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
32 changes: 16 additions & 16 deletions tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(12,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(13,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(14,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(12,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(13,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(14,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(30,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(31,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(32,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
Expand All @@ -15,7 +15,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,17): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,11): error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,17): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter


Expand All @@ -33,27 +33,27 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv

var ra1 = a1 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra2 = a2 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra3 = a3 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra4 = a4 in x;
var ra5 = null in x;
~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra6 = undefined in x;
~~~~~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra7 = E.a in x;
var ra8 = false in x;
~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra9 = {} in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.

// invalid right operands
// the right operand is required to be of type Any, an object type, or a type parameter type
Expand Down Expand Up @@ -98,6 +98,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
// both operands are invalid
var rc1 = {} in '';
~~
!!! error TS2360: The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
~~
!!! error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
9 changes: 9 additions & 0 deletions tests/baselines/reference/symbolProperty54.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
tests/cases/conformance/es6/Symbols/symbolProperty54.ts(2,5): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.


==== tests/cases/conformance/es6/Symbols/symbolProperty54.ts (1 errors) ====
var obj = {
[Symbol.prototype]: 0
~~~~~~~~~~~~~~~~~~
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
};
Loading

0 comments on commit 2d16474

Please sign in to comment.