Skip to content

Commit 0b125a3

Browse files
authored
Merge pull request #11990 from HerringtonDarkholme/delete-readonly
fix #11480, disallow delete operator on readonly property or index signature
2 parents 20097d7 + 634dff2 commit 0b125a3

28 files changed

+774
-16
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6072,7 +6072,7 @@ namespace ts {
60726072
getIndexInfoOfType(objectType, IndexKind.String) ||
60736073
undefined;
60746074
if (indexInfo) {
6075-
if (accessExpression && isAssignmentTarget(accessExpression) && indexInfo.isReadonly) {
6075+
if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
60766076
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
60776077
return unknownType;
60786078
}
@@ -14386,6 +14386,16 @@ namespace ts {
1438614386

1438714387
function checkDeleteExpression(node: DeleteExpression): Type {
1438814388
checkExpression(node.expression);
14389+
const expr = skipParentheses(node.expression);
14390+
if (expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) {
14391+
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
14392+
return booleanType;
14393+
}
14394+
const links = getNodeLinks(expr);
14395+
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
14396+
if (symbol && isReadonlySymbol(symbol)) {
14397+
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
14398+
}
1438914399
return booleanType;
1439014400
}
1439114401

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,14 @@
20192019
"category": "Error",
20202020
"code": 2702
20212021
},
2022+
"The operand of a delete operator must be a property reference": {
2023+
"category": "Error",
2024+
"code": 2703
2025+
},
2026+
"The operand of a delete operator cannot be a read-only property": {
2027+
"category": "Error",
2028+
"code": 2704
2029+
},
20222030

20232031
"Import declaration '{0}' is using private name '{1}'.": {
20242032
"category": "Error",

src/compiler/utilities.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// <reference path="sys.ts" />
1+
/// <reference path="sys.ts" />
22

33
/* @internal */
44
namespace ts {
@@ -1668,6 +1668,18 @@ namespace ts {
16681668
return getAssignmentTargetKind(node) !== AssignmentKind.None;
16691669
}
16701670

1671+
// a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped
1672+
export function isDeleteTarget(node: Node): boolean {
1673+
if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
1674+
return false;
1675+
}
1676+
node = node.parent;
1677+
while (node && node.kind === SyntaxKind.ParenthesizedExpression) {
1678+
node = node.parent;
1679+
}
1680+
return node && node.kind === SyntaxKind.DeleteExpression;
1681+
}
1682+
16711683
export function isNodeDescendantOf(node: Node, ancestor: Node): boolean {
16721684
while (node) {
16731685
if (node === ancestor) return true;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/conformance/async/es2017/await_unaryExpression_es2017_1.ts(7,12): error TS2703: The operand of a delete operator must be a property reference
2+
tests/cases/conformance/async/es2017/await_unaryExpression_es2017_1.ts(11,12): error TS2703: The operand of a delete operator must be a property reference
3+
4+
5+
==== tests/cases/conformance/async/es2017/await_unaryExpression_es2017_1.ts (2 errors) ====
6+
7+
async function bar() {
8+
!await 42; // OK
9+
}
10+
11+
async function bar1() {
12+
delete await 42; // OK
13+
~~~~~~~~
14+
!!! error TS2703: The operand of a delete operator must be a property reference
15+
}
16+
17+
async function bar2() {
18+
delete await 42; // OK
19+
~~~~~~~~
20+
!!! error TS2703: The operand of a delete operator must be a property reference
21+
}
22+
23+
async function bar3() {
24+
void await 42;
25+
}
26+
27+
async function bar4() {
28+
+await 42;
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/conformance/async/es2017/await_unaryExpression_es2017_2.ts(3,12): error TS2703: The operand of a delete operator must be a property reference
2+
tests/cases/conformance/async/es2017/await_unaryExpression_es2017_2.ts(7,12): error TS2703: The operand of a delete operator must be a property reference
3+
4+
5+
==== tests/cases/conformance/async/es2017/await_unaryExpression_es2017_2.ts (2 errors) ====
6+
7+
async function bar1() {
8+
delete await 42;
9+
~~~~~~~~
10+
!!! error TS2703: The operand of a delete operator must be a property reference
11+
}
12+
13+
async function bar2() {
14+
delete await 42;
15+
~~~~~~~~
16+
!!! error TS2703: The operand of a delete operator must be a property reference
17+
}
18+
19+
async function bar3() {
20+
void await 42;
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts(7,12): error TS2703: The operand of a delete operator must be a property reference
2+
tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts(11,12): error TS2703: The operand of a delete operator must be a property reference
3+
4+
5+
==== tests/cases/conformance/async/es6/await_unaryExpression_es6_1.ts (2 errors) ====
6+
7+
async function bar() {
8+
!await 42; // OK
9+
}
10+
11+
async function bar1() {
12+
delete await 42; // OK
13+
~~~~~~~~
14+
!!! error TS2703: The operand of a delete operator must be a property reference
15+
}
16+
17+
async function bar2() {
18+
delete await 42; // OK
19+
~~~~~~~~
20+
!!! error TS2703: The operand of a delete operator must be a property reference
21+
}
22+
23+
async function bar3() {
24+
void await 42;
25+
}
26+
27+
async function bar4() {
28+
+await 42;
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts(3,12): error TS2703: The operand of a delete operator must be a property reference
2+
tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts(7,12): error TS2703: The operand of a delete operator must be a property reference
3+
4+
5+
==== tests/cases/conformance/async/es6/await_unaryExpression_es6_2.ts (2 errors) ====
6+
7+
async function bar1() {
8+
delete await 42;
9+
~~~~~~~~
10+
!!! error TS2703: The operand of a delete operator must be a property reference
11+
}
12+
13+
async function bar2() {
14+
delete await 42;
15+
~~~~~~~~
16+
!!! error TS2703: The operand of a delete operator must be a property reference
17+
}
18+
19+
async function bar3() {
20+
void await 42;
21+
}

tests/baselines/reference/computedPropertyNames3_ES5.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(4,1
22
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(5,9): error TS2378: A 'get' accessor must return a value.
33
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
44
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(5,17): error TS1102: 'delete' cannot be called on an identifier in strict mode.
5+
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(5,17): error TS2703: The operand of a delete operator must be a property reference
56
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(6,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
67
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(7,16): error TS2378: A 'get' accessor must return a value.
78
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(7,16): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
89

910

10-
==== tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts (7 errors) ====
11+
==== tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts (8 errors) ====
1112
var id;
1213
class C {
1314
[0 + 1]() { }
@@ -21,6 +22,8 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES5.ts(7,1
2122
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
2223
~~
2324
!!! error TS1102: 'delete' cannot be called on an identifier in strict mode.
25+
~~
26+
!!! error TS2703: The operand of a delete operator must be a property reference
2427
set [[0, 1]](v) { }
2528
~~~~~~~~
2629
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

tests/baselines/reference/computedPropertyNames3_ES6.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(4,1
22
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(5,9): error TS2378: A 'get' accessor must return a value.
33
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
44
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(5,17): error TS1102: 'delete' cannot be called on an identifier in strict mode.
5+
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(5,17): error TS2703: The operand of a delete operator must be a property reference
56
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(6,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
67
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(7,16): error TS2378: A 'get' accessor must return a value.
78
tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(7,16): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
89

910

10-
==== tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts (7 errors) ====
11+
==== tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts (8 errors) ====
1112
var id;
1213
class C {
1314
[0 + 1]() { }
@@ -21,6 +22,8 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames3_ES6.ts(7,1
2122
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
2223
~~
2324
!!! error TS1102: 'delete' cannot be called on an identifier in strict mode.
25+
~~
26+
!!! error TS2703: The operand of a delete operator must be a property reference
2427
set [[0, 1]](v) { }
2528
~~~~~~~~
2629
!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts(15,12): error TS2703: The operand of a delete operator must be a property reference
2+
3+
4+
==== tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts (1 errors) ====
5+
6+
function f() {
7+
let x: { a?: number | string, b: number | string } = { b: 1 };
8+
x.a;
9+
x.b;
10+
x.a = 1;
11+
x.b = 1;
12+
x.a;
13+
x.b;
14+
delete x.a;
15+
delete x.b;
16+
x.a;
17+
x.b;
18+
x;
19+
delete x; // No effect
20+
~
21+
!!! error TS2703: The operand of a delete operator must be a property reference
22+
x;
23+
}

0 commit comments

Comments
 (0)