Skip to content

Commit

Permalink
Correctly compute noUncheckedIndexedAccess effects on compound/increm…
Browse files Browse the repository at this point in the history
…ent/decrement assignments (#58239)
  • Loading branch information
RyanCavanaugh authored Apr 19, 2024
1 parent 3480321 commit aedd1b1
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 4 deletions.
19 changes: 15 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33031,7 +33031,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}

propType = (compilerOptions.noUncheckedIndexedAccess && !isAssignmentTarget(node)) ? getUnionType([indexInfo.type, missingType]) : indexInfo.type;
propType = indexInfo.type;
if (compilerOptions.noUncheckedIndexedAccess && getAssignmentTargetKind(node) !== AssignmentKind.Definite) {
propType = getUnionType([propType, missingType]);
}
if (compilerOptions.noPropertyAccessFromIndexSignature && isPropertyAccessExpression(node)) {
error(right, Diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, unescapeLeadingUnderscores(right.escapedText));
}
Expand Down Expand Up @@ -33626,9 +33629,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType;
const accessFlags = isAssignmentTarget(node) ?
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
AccessFlags.ExpressionPosition;
const assignmentTargetKind = getAssignmentTargetKind(node);
let accessFlags: AccessFlags;
if (assignmentTargetKind === AssignmentKind.None) {
accessFlags = AccessFlags.ExpressionPosition;
}
else {
accessFlags = AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0);
if (assignmentTargetKind === AssignmentKind.Compound) {
accessFlags |= AccessFlags.ExpressionPosition;
}
}
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, accessFlags, node) || errorType;
return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, getNodeLinks(node).resolvedSymbol, indexedAccessType, indexExpression, checkMode), node);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
noUncheckedIndexedAccessCompoundAssignments.ts(3,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(4,3): error TS18048: 'stringMap.foo' is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(5,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(6,1): error TS18048: 'stringMap.foo' is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(7,3): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(8,1): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(9,3): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(10,1): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(11,1): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(12,1): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(13,1): error TS2532: Object is possibly 'undefined'.
noUncheckedIndexedAccessCompoundAssignments.ts(14,1): error TS2532: Object is possibly 'undefined'.


==== noUncheckedIndexedAccessCompoundAssignments.ts (12 errors) ====
// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
~~~~~~~~~~~~~
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
--stringMap.foo;
~~~~~~~~~~~~~
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
stringMap.foo += 1;
~~~~~~~~~~~~~
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
stringMap.foo *= 1;
~~~~~~~~~~~~~
!!! error TS18048: 'stringMap.foo' is possibly 'undefined'.
++stringMap['foo'];
~~~~~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
stringMap['foo']--;
~~~~~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
++stringMap[s];
~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
stringMap[s]--;
~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
numberMap[32]++;
~~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
numberMap[32] += 1;
~~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
numberMap[n]++;
~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
numberMap[n] += 1;
~~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.

declare const stringMap: { [s: string]: number };
declare const s: string;
declare const numberMap: { [n: number]: number };
declare const n: number;

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////

//// [noUncheckedIndexedAccessCompoundAssignments.ts]
// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
--stringMap.foo;
stringMap.foo += 1;
stringMap.foo *= 1;
++stringMap['foo'];
stringMap['foo']--;
++stringMap[s];
stringMap[s]--;
numberMap[32]++;
numberMap[32] += 1;
numberMap[n]++;
numberMap[n] += 1;

declare const stringMap: { [s: string]: number };
declare const s: string;
declare const numberMap: { [n: number]: number };
declare const n: number;


//// [noUncheckedIndexedAccessCompoundAssignments.js]
"use strict";
// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
--stringMap.foo;
stringMap.foo += 1;
stringMap.foo *= 1;
++stringMap['foo'];
stringMap['foo']--;
++stringMap[s];
stringMap[s]--;
numberMap[32]++;
numberMap[32] += 1;
numberMap[n]++;
numberMap[n] += 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////

=== noUncheckedIndexedAccessCompoundAssignments.ts ===
// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))

--stringMap.foo;
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))

stringMap.foo += 1;
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))

stringMap.foo *= 1;
>stringMap.foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>foo : Symbol(__index, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 26))

++stringMap['foo'];
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))

stringMap['foo']--;
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))

++stringMap[s];
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))

stringMap[s]--;
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))

numberMap[32]++;
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))

numberMap[32] += 1;
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))

numberMap[n]++;
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))

numberMap[n] += 1;
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))

declare const stringMap: { [s: string]: number };
>stringMap : Symbol(stringMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 13))
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 15, 28))

declare const s: string;
>s : Symbol(s, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 16, 13))

declare const numberMap: { [n: number]: number };
>numberMap : Symbol(numberMap, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 13))
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 17, 28))

declare const n: number;
>n : Symbol(n, Decl(noUncheckedIndexedAccessCompoundAssignments.ts, 18, 13))

Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//// [tests/cases/compiler/noUncheckedIndexedAccessCompoundAssignments.ts] ////

=== noUncheckedIndexedAccessCompoundAssignments.ts ===
// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
>stringMap.foo++ : number
> : ^^^^^^
>stringMap.foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^

--stringMap.foo;
>--stringMap.foo : number
> : ^^^^^^
>stringMap.foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^

stringMap.foo += 1;
>stringMap.foo += 1 : number
> : ^^^^^^
>stringMap.foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>1 : 1
> : ^

stringMap.foo *= 1;
>stringMap.foo *= 1 : number
> : ^^^^^^
>stringMap.foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>foo : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>1 : 1
> : ^

++stringMap['foo'];
>++stringMap['foo'] : number
> : ^^^^^^
>stringMap['foo'] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>'foo' : "foo"
> : ^^^^^

stringMap['foo']--;
>stringMap['foo']-- : number
> : ^^^^^^
>stringMap['foo'] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>'foo' : "foo"
> : ^^^^^

++stringMap[s];
>++stringMap[s] : number
> : ^^^^^^
>stringMap[s] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>s : string
> : ^^^^^^

stringMap[s]--;
>stringMap[s]-- : number
> : ^^^^^^
>stringMap[s] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>s : string
> : ^^^^^^

numberMap[32]++;
>numberMap[32]++ : number
> : ^^^^^^
>numberMap[32] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>numberMap : { [n: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>32 : 32
> : ^^

numberMap[32] += 1;
>numberMap[32] += 1 : number
> : ^^^^^^
>numberMap[32] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>numberMap : { [n: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>32 : 32
> : ^^
>1 : 1
> : ^

numberMap[n]++;
>numberMap[n]++ : number
> : ^^^^^^
>numberMap[n] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>numberMap : { [n: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>n : number
> : ^^^^^^

numberMap[n] += 1;
>numberMap[n] += 1 : number
> : ^^^^^^
>numberMap[n] : number | undefined
> : ^^^^^^^^^^^^^^^^^^
>numberMap : { [n: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>n : number
> : ^^^^^^
>1 : 1
> : ^

declare const stringMap: { [s: string]: number };
>stringMap : { [s: string]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>s : string
> : ^^^^^^

declare const s: string;
>s : string
> : ^^^^^^

declare const numberMap: { [n: number]: number };
>numberMap : { [n: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>n : number
> : ^^^^^^

declare const n: number;
>n : number
> : ^^^^^^

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @strict: true
// @noUncheckedIndexedAccess: true

// Each line should have one error
// for a total of 12 errors
stringMap.foo++;
--stringMap.foo;
stringMap.foo += 1;
stringMap.foo *= 1;
++stringMap['foo'];
stringMap['foo']--;
++stringMap[s];
stringMap[s]--;
numberMap[32]++;
numberMap[32] += 1;
numberMap[n]++;
numberMap[n] += 1;

declare const stringMap: { [s: string]: number };
declare const s: string;
declare const numberMap: { [n: number]: number };
declare const n: number;

0 comments on commit aedd1b1

Please sign in to comment.