Skip to content

Commit 7c86076

Browse files
thomastanckmartin-henz
authored andcommitted
(RFC) utils/rttc.ts: Add integer check for array indices (#282)
* utils/rttc.ts: Add integer check for array indices * Fix allowed range of array indices * Use term "array index" in error message and add detail for other numbers
1 parent b0a96ef commit 7c86076

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

src/utils/__tests__/__snapshots__/rttc.ts.snap

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8317,8 +8317,8 @@ Object {
83178317

83188318
exports[`Member expression type combinations: Invalid type combinations return TypeError 54`] = `
83198319
Object {
8320-
"elaborate": "Expected number as prop, got boolean.",
8321-
"explain": "Expected number as prop, got boolean.",
8320+
"elaborate": "Expected array index as prop, got boolean.",
8321+
"explain": "Expected array index as prop, got boolean.",
83228322
"left": Array [
83238323
2,
83248324
],
@@ -8328,8 +8328,8 @@ Object {
83288328

83298329
exports[`Member expression type combinations: Invalid type combinations return TypeError 55`] = `
83308330
Object {
8331-
"elaborate": "Expected number as prop, got string.",
8332-
"explain": "Expected number as prop, got string.",
8331+
"elaborate": "Expected array index as prop, got string.",
8332+
"explain": "Expected array index as prop, got string.",
83338333
"left": Array [
83348334
2,
83358335
],
@@ -8339,8 +8339,8 @@ Object {
83398339

83408340
exports[`Member expression type combinations: Invalid type combinations return TypeError 56`] = `
83418341
Object {
8342-
"elaborate": "Expected number as prop, got function.",
8343-
"explain": "Expected number as prop, got function.",
8342+
"elaborate": "Expected array index as prop, got function.",
8343+
"explain": "Expected array index as prop, got function.",
83448344
"left": Array [
83458345
2,
83468346
],
@@ -8350,8 +8350,8 @@ Object {
83508350

83518351
exports[`Member expression type combinations: Invalid type combinations return TypeError 57`] = `
83528352
Object {
8353-
"elaborate": "Expected number as prop, got function.",
8354-
"explain": "Expected number as prop, got function.",
8353+
"elaborate": "Expected array index as prop, got function.",
8354+
"explain": "Expected array index as prop, got function.",
83558355
"left": Array [
83568356
2,
83578357
],
@@ -8361,8 +8361,8 @@ Object {
83618361

83628362
exports[`Member expression type combinations: Invalid type combinations return TypeError 58`] = `
83638363
Object {
8364-
"elaborate": "Expected number as prop, got object.",
8365-
"explain": "Expected number as prop, got object.",
8364+
"elaborate": "Expected array index as prop, got object.",
8365+
"explain": "Expected array index as prop, got object.",
83668366
"left": Array [
83678367
2,
83688368
],
@@ -8374,8 +8374,8 @@ Object {
83748374

83758375
exports[`Member expression type combinations: Invalid type combinations return TypeError 59`] = `
83768376
Object {
8377-
"elaborate": "Expected number as prop, got array.",
8378-
"explain": "Expected number as prop, got array.",
8377+
"elaborate": "Expected array index as prop, got array.",
8378+
"explain": "Expected array index as prop, got array.",
83798379
"left": Array [
83808380
2,
83818381
],
@@ -8387,8 +8387,8 @@ Object {
83878387

83888388
exports[`Member expression type combinations: Invalid type combinations return TypeError 60`] = `
83898389
Object {
8390-
"elaborate": "Expected number as prop, got undefined.",
8391-
"explain": "Expected number as prop, got undefined.",
8390+
"elaborate": "Expected array index as prop, got undefined.",
8391+
"explain": "Expected array index as prop, got undefined.",
83928392
"left": Array [
83938393
2,
83948394
],
@@ -8398,8 +8398,8 @@ Object {
83988398

83998399
exports[`Member expression type combinations: Invalid type combinations return TypeError 61`] = `
84008400
Object {
8401-
"elaborate": "Expected number as prop, got null.",
8402-
"explain": "Expected number as prop, got null.",
8401+
"elaborate": "Expected array index as prop, got null.",
8402+
"explain": "Expected array index as prop, got null.",
84038403
"left": Array [
84048404
2,
84058405
],
@@ -8577,6 +8577,39 @@ Object {
85778577
}
85788578
`;
85798579

8580+
exports[`Member expression type combinations: Invalid type combinations return TypeError 80`] = `
8581+
Object {
8582+
"elaborate": "Expected array index as prop, got other number.",
8583+
"explain": "Expected array index as prop, got other number.",
8584+
"left": Array [
8585+
2,
8586+
],
8587+
"right": -1,
8588+
}
8589+
`;
8590+
8591+
exports[`Member expression type combinations: Invalid type combinations return TypeError 81`] = `
8592+
Object {
8593+
"elaborate": "Expected array index as prop, got other number.",
8594+
"explain": "Expected array index as prop, got other number.",
8595+
"left": Array [
8596+
2,
8597+
],
8598+
"right": 0.5,
8599+
}
8600+
`;
8601+
8602+
exports[`Member expression type combinations: Invalid type combinations return TypeError 82`] = `
8603+
Object {
8604+
"elaborate": "Expected array index as prop, got other number.",
8605+
"explain": "Expected array index as prop, got other number.",
8606+
"left": Array [
8607+
2,
8608+
],
8609+
"right": 4294967295,
8610+
}
8611+
`;
8612+
85808613
exports[`Ternary/if test expression type combinations: Invalid type combinations return TypeError 1`] = `
85818614
Object {
85828615
"elaborate": "Expected boolean as condition, got number.",

src/utils/__tests__/rttc.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ describe('Member expression type combinations:', () => {
289289
})
290290
})
291291

292+
// Extra tests for array indices integral check.
293+
valid.push([arr, 0])
294+
valid.push([arr, 10])
295+
valid.push([arr, 2 ** 32 - 2])
296+
invalid.push([arr, -1])
297+
invalid.push([arr, 0.5])
298+
invalid.push([arr, 2 ** 32 - 1])
299+
292300
test('Valid type combinations are OK', () => {
293301
valid.forEach(([left, right]: [Value, Value]) => {
294302
const context = mockRuntimeContext()

src/utils/rttc.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ const typeOf = (v: Value) => {
3535
}
3636

3737
const isNumber = (v: Value) => typeOf(v) === 'number'
38+
// See section 4 of https://2ality.com/2012/12/arrays.html
39+
// v >>> 0 === v checks that v is a valid unsigned 32-bit int
40+
// tslint:disable-next-line:no-bitwise
41+
const isArrayIndex = (v: Value) => isNumber(v) && v >>> 0 === v && v < 2 ** 32 - 1
3842
const isString = (v: Value) => typeOf(v) === 'string'
3943
const isBool = (v: Value) => typeOf(v) === 'boolean'
4044
const isObject = (v: Value) => typeOf(v) === 'object'
@@ -95,7 +99,11 @@ export const checkMemberAccess = (node: es.Node, obj: Value, prop: Value) => {
9599
if (isObject(obj)) {
96100
return isString(prop) ? undefined : new TypeError(node, ' as prop', 'string', typeOf(prop))
97101
} else if (isArray(obj)) {
98-
return isNumber(prop) ? undefined : new TypeError(node, ' as prop', 'number', typeOf(prop))
102+
return isArrayIndex(prop)
103+
? undefined
104+
: isNumber(prop)
105+
? new TypeError(node, ' as prop', 'array index', 'other number')
106+
: new TypeError(node, ' as prop', 'array index', typeOf(prop))
99107
} else {
100108
return new TypeError(node, '', 'object or array', typeOf(obj))
101109
}

0 commit comments

Comments
 (0)