Add related diagnostic to "used before defined" if type is a function that returns a union with undefined (#33171)

* Add "use before defined" diagnostic

* Make "use before defined" diagnostic as related information to TS2454

* Add baseline tests for "use before defined"

* Add test for type alias union with undefined for "use before defined" diagnostic

* Update baselines
This commit is contained in:
Ozair Patel 2019-11-04 16:53:31 -06:00 committed by Orta
parent 47ec514cf4
commit be960fa356
7 changed files with 369 additions and 1 deletions

View File

@ -20190,7 +20190,26 @@ namespace ts {
}
}
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
const diag = error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// See GH:32846 - if the user is using a variable whose type is () => T1 | ... | undefined
// they may have meant to specify the type as (() => T1 | ...) | undefined
// This is assumed if: the type is a FunctionType, the return type is a Union, the last constituent of
// the union is `undefined`
if (type.symbol && type.symbol.declarations.length === 1 && isFunctionTypeNode(type.symbol.declarations[0])) {
const funcTypeNode = <FunctionTypeNode>type.symbol.declarations[0];
const returnType = getReturnTypeFromAnnotation(funcTypeNode);
if (returnType && returnType.flags & TypeFlags.Union) {
const unionTypes = (<UnionTypeNode>funcTypeNode.type).types;
if (unionTypes && unionTypes[unionTypes.length - 1].kind === SyntaxKind.UndefinedKeyword) {
const parenedFuncType = getMutableClone(funcTypeNode);
// Highlight to the end of the second to last constituent of the union
parenedFuncType.end = unionTypes[unionTypes.length - 2].end;
addRelatedInfo(diag, createDiagnosticForNode(parenedFuncType, Diagnostics.Did_you_mean_to_parenthesize_this_function_type));
}
}
}
// Return the declared type to reduce follow-on errors
return type;
}

View File

@ -1047,6 +1047,10 @@
"category": "Error",
"code": 1359
},
"Did you mean to parenthesize this function type?": {
"category": "Error",
"code": 1360
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",

View File

@ -0,0 +1,74 @@
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(2,1): error TS2454: Variable 'a' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(6,1): error TS2454: Variable 'c' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(10,1): error TS2454: Variable 'd' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(13,1): error TS2454: Variable 'e' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(16,1): error TS2454: Variable 'f' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(19,1): error TS2454: Variable 'g' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(24,1): error TS2454: Variable 'h' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(28,1): error TS2454: Variable 'i' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(32,1): error TS2454: Variable 'j' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(36,1): error TS2454: Variable 'j' is used before being assigned.
==== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts (10 errors) ====
let a: () => void | undefined;
a; // Error did you mean to paren this function type
~
!!! error TS2454: Variable 'a' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:1:8: Did you mean to parenthesize this function type?
function b (): void | undefined {}
let c: typeof b;
c;
~
!!! error TS2454: Variable 'c' is used before being assigned.
type Undefined = undefined
let d: () => void | Undefined;
d;
~
!!! error TS2454: Variable 'd' is used before being assigned.
let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...
~
!!! error TS2454: Variable 'e' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:12:8: Did you mean to parenthesize this function type?
let f: () => void;
f;
~
!!! error TS2454: Variable 'f' is used before being assigned.
let g: () => undefined;
g;
~
!!! error TS2454: Variable 'g' is used before being assigned.
type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;
~
!!! error TS2454: Variable 'h' is used before being assigned.
type T3 = void | undefined;
let i: () => T3;
i;
~
!!! error TS2454: Variable 'i' is used before being assigned.
type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...
~
!!! error TS2454: Variable 'j' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?
type T5 = () => void
let k: T5 | undefined
j;
~
!!! error TS2454: Variable 'j' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?

View File

@ -0,0 +1,61 @@
//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts]
let a: () => void | undefined;
a; // Error did you mean to paren this function type
function b (): void | undefined {}
let c: typeof b;
c;
type Undefined = undefined
let d: () => void | Undefined;
d;
let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...
let f: () => void;
f;
let g: () => undefined;
g;
type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;
type T3 = void | undefined;
let i: () => T3;
i;
type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...
type T5 = () => void
let k: T5 | undefined
j;
//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.js]
var a;
a; // Error did you mean to paren this function type
function b() { }
var c;
c;
var d;
d;
var e;
e; // Error did you mean to paren...
var f;
f;
var g;
g;
var h;
h;
var i;
i;
var j;
j; // Error did you mean to paren...
var k;
j;

View File

@ -0,0 +1,89 @@
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
let a: () => void | undefined;
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))
a; // Error did you mean to paren this function type
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))
function b (): void | undefined {}
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))
let c: typeof b;
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))
c;
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))
type Undefined = undefined
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))
let d: () => void | Undefined;
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))
d;
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))
let e: () => string | void | number | object | undefined;
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))
e; // Error did you mean to paren...
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))
let f: () => void;
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))
f;
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))
let g: () => undefined;
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))
g;
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))
type T1 = undefined | string;
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))
type T2 = undefined | void;
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))
let h: () => T1 & T2;
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))
h;
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))
type T3 = void | undefined;
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))
let i: () => T3;
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))
i;
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))
type T4 = () => void | undefined;
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))
let j: T4;
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))
j; // Error did you mean to paren...
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
type T5 = () => void
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))
let k: T5 | undefined
>k : Symbol(k, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 34, 3))
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))
j;
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))

View File

@ -0,0 +1,83 @@
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
let a: () => void | undefined;
>a : () => void | undefined
a; // Error did you mean to paren this function type
>a : () => void | undefined
function b (): void | undefined {}
>b : () => void | undefined
let c: typeof b;
>c : () => void | undefined
>b : () => void | undefined
c;
>c : () => void | undefined
type Undefined = undefined
>Undefined : undefined
let d: () => void | Undefined;
>d : () => void | undefined
d;
>d : () => void | undefined
let e: () => string | void | number | object | undefined;
>e : () => string | number | void | object | undefined
e; // Error did you mean to paren...
>e : () => string | number | void | object | undefined
let f: () => void;
>f : () => void
f;
>f : () => void
let g: () => undefined;
>g : () => undefined
g;
>g : () => undefined
type T1 = undefined | string;
>T1 : T1
type T2 = undefined | void;
>T2 : void | undefined
let h: () => T1 & T2;
>h : () => undefined
h;
>h : () => undefined
type T3 = void | undefined;
>T3 : void | undefined
let i: () => T3;
>i : () => void | undefined
i;
>i : () => void | undefined
type T4 = () => void | undefined;
>T4 : T4
let j: T4;
>j : T4
j; // Error did you mean to paren...
>j : T4
type T5 = () => void
>T5 : T5
let k: T5 | undefined
>k : T5 | undefined
j;
>j : T4

View File

@ -0,0 +1,38 @@
// @strictNullChecks: true
let a: () => void | undefined;
a; // Error did you mean to paren this function type
function b (): void | undefined {}
let c: typeof b;
c;
type Undefined = undefined
let d: () => void | Undefined;
d;
let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...
let f: () => void;
f;
let g: () => undefined;
g;
type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;
type T3 = void | undefined;
let i: () => T3;
i;
type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...
type T5 = () => void
let k: T5 | undefined
j;