Skip to content

Commit 7fe1aba

Browse files
author
Andy
authored
Merge pull request microsoft#15557 from Microsoft/unused-type-parameter
Mark type arguments as used even if used in an invalid way
2 parents 500dd16 + 8d8c4c2 commit 7fe1aba

24 files changed

+540
-37
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4714,7 +4714,7 @@ namespace ts {
47144714
// When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the
47154715
// class and all return the instance type of the class. There is no need for further checks and we can apply the
47164716
// type arguments in the same manner as a type reference to get the same error reporting experience.
4717-
baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol);
4717+
baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol, typeArgumentsFromTypeReferenceNode(baseTypeNode));
47184718
}
47194719
else if (baseConstructorType.flags & TypeFlags.Any) {
47204720
baseType = baseConstructorType;
@@ -5238,7 +5238,7 @@ namespace ts {
52385238
}
52395239
const baseTypeNode = getBaseTypeNodeOfClass(classType);
52405240
const isJavaScript = isInJavaScriptFile(baseTypeNode);
5241-
const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
5241+
const typeArguments = typeArgumentsFromTypeReferenceNode(baseTypeNode);
52425242
const typeArgCount = length(typeArguments);
52435243
const result: Signature[] = [];
52445244
for (const baseSig of baseSignatures) {
@@ -6501,7 +6501,7 @@ namespace ts {
65016501
}
65026502

65036503
// Get type from reference to class or interface
6504-
function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type {
6504+
function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol, typeArgs: Type[]): Type {
65056505
const type = <InterfaceType>getDeclaredTypeOfSymbol(getMergedSymbol(symbol));
65066506
const typeParameters = type.localTypeParameters;
65076507
if (typeParameters) {
@@ -6520,7 +6520,7 @@ namespace ts {
65206520
// In a type reference, the outer type parameters of the referenced class or interface are automatically
65216521
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
65226522
// of the class or interface.
6523-
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(map(node.typeArguments, getTypeFromTypeNode), typeParameters, minTypeArgumentCount, node));
6523+
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node));
65246524
return createTypeReference(<GenericType>type, typeArguments);
65256525
}
65266526
if (node.typeArguments) {
@@ -6545,7 +6545,7 @@ namespace ts {
65456545
// Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include
65466546
// references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
65476547
// declared type. Instantiations are cached using the type identities of the type arguments as the key.
6548-
function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type {
6548+
function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol, typeArguments: Type[]): Type {
65496549
const type = getDeclaredTypeOfSymbol(symbol);
65506550
const typeParameters = getSymbolLinks(symbol).typeParameters;
65516551
if (typeParameters) {
@@ -6561,7 +6561,6 @@ namespace ts {
65616561
typeParameters.length);
65626562
return unknownType;
65636563
}
6564-
const typeArguments = map(node.typeArguments, getTypeFromTypeNode);
65656564
return getTypeAliasInstantiation(symbol, typeArguments);
65666565
}
65676566
if (node.typeArguments) {
@@ -6609,16 +6608,18 @@ namespace ts {
66096608
}
66106609

66116610
function getTypeReferenceType(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol) {
6611+
const typeArguments = typeArgumentsFromTypeReferenceNode(node); // Do unconditionally so we mark type arguments as referenced.
6612+
66126613
if (symbol === unknownSymbol) {
66136614
return unknownType;
66146615
}
66156616

66166617
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
6617-
return getTypeFromClassOrInterfaceReference(node, symbol);
6618+
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
66186619
}
66196620

66206621
if (symbol.flags & SymbolFlags.TypeAlias) {
6621-
return getTypeFromTypeAliasReference(node, symbol);
6622+
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
66226623
}
66236624

66246625
if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) {
@@ -6686,10 +6687,7 @@ namespace ts {
66866687
? <EntityNameExpression>(<ExpressionWithTypeArguments>node).expression
66876688
: undefined;
66886689
symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol;
6689-
type = symbol === unknownSymbol ? unknownType :
6690-
symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) :
6691-
symbol.flags & SymbolFlags.TypeAlias ? getTypeFromTypeAliasReference(node, symbol) :
6692-
getTypeFromNonGenericTypeReference(node, symbol);
6690+
type = getTypeReferenceType(node, symbol);
66936691
}
66946692
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
66956693
// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
@@ -6699,6 +6697,10 @@ namespace ts {
66996697
return links.resolvedType;
67006698
}
67016699

6700+
function typeArgumentsFromTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): Type[] {
6701+
return map(node.typeArguments, getTypeFromTypeNode);
6702+
}
6703+
67026704
function getTypeFromTypeQueryNode(node: TypeQueryNode): Type {
67036705
const links = getNodeLinks(node);
67046706
if (!links.resolvedType) {
@@ -14318,7 +14320,18 @@ namespace ts {
1431814320
return true;
1431914321
}
1432014322

14323+
function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression {
14324+
// TODO: Also include tagged templates (https://github.com/Microsoft/TypeScript/issues/11947)
14325+
return isCallOrNewExpression(node);
14326+
}
14327+
1432114328
function resolveUntypedCall(node: CallLikeExpression): Signature {
14329+
if (callLikeExpressionMayHaveTypeArguments(node)) {
14330+
// Check type arguments even though we will give an error that untyped calls may not accept type arguments.
14331+
// This gets us diagnostics for the type arguments and marks them as referenced.
14332+
forEach(node.typeArguments, checkSourceElement);
14333+
}
14334+
1432214335
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
1432314336
checkExpression((<TaggedTemplateExpression>node).template);
1432414337
}

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,10 @@ namespace ts {
11421142
}
11431143
}
11441144

1145+
export function isCallOrNewExpression(node: Node): node is CallExpression | NewExpression {
1146+
return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression;
1147+
}
1148+
11451149
export function getInvokedExpression(node: CallLikeExpression): Expression {
11461150
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
11471151
return (<TaggedTemplateExpression>node).tag;

src/services/formatting/smartIndenter.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,7 @@ namespace ts.formatting {
341341
return Value.Unknown;
342342
}
343343

344-
if (node.parent && (
345-
node.parent.kind === SyntaxKind.CallExpression ||
346-
node.parent.kind === SyntaxKind.NewExpression) &&
347-
(<CallExpression>node.parent).expression !== node) {
344+
if (node.parent && isCallOrNewExpression(node.parent) && (<CallExpression>node.parent).expression !== node) {
348345

349346
const fullCallOrNewExpression = (<CallExpression | NewExpression>node.parent).expression;
350347
const startingExpression = getStartingExpression(fullCallOrNewExpression);

src/services/signatureHelp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ namespace ts.SignatureHelp {
262262
* in the argument of an invocation; returns undefined otherwise.
263263
*/
264264
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
265-
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
265+
if (isCallOrNewExpression(node.parent)) {
266266
const callExpression = <CallExpression>node.parent;
267267
// There are 3 cases to handle:
268268
// 1. The token introduces a list, and should begin a signature help session

src/services/symbolDisplay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ namespace ts.SymbolDisplay {
120120

121121
// try get the call/construct signature from the type if it matches
122122
let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement;
123-
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
123+
if (isCallOrNewExpression(location)) {
124124
callExpressionLike = <CallExpression | NewExpression>location;
125125
}
126126
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts(1,1): error TS2304: Cannot find name 'Foo'.
2+
tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts(1,5): error TS2304: Cannot find name 'a'.
23
tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts(1,7): error TS1110: Type expected.
4+
tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts(1,8): error TS2304: Cannot find name 'b'.
35

46

5-
==== tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts (2 errors) ====
7+
==== tests/cases/compiler/callExpressionWithMissingTypeArgument1.ts (4 errors) ====
68
Foo<a,,b>();
79
~~~
810
!!! error TS2304: Cannot find name 'Foo'.
11+
~
12+
!!! error TS2304: Cannot find name 'a'.
913
~
10-
!!! error TS1110: Type expected.
14+
!!! error TS1110: Type expected.
15+
~
16+
!!! error TS2304: Cannot find name 'b'.

tests/baselines/reference/functionCalls.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(8,1): error TS2347: Untyped function calls may not accept type arguments.
22
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(9,1): error TS2347: Untyped function calls may not accept type arguments.
33
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(10,1): error TS2347: Untyped function calls may not accept type arguments.
4+
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(10,8): error TS2304: Cannot find name 'Window'.
45
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(25,1): error TS2347: Untyped function calls may not accept type arguments.
56
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(26,1): error TS2347: Untyped function calls may not accept type arguments.
67
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(27,1): error TS2347: Untyped function calls may not accept type arguments.
@@ -9,7 +10,7 @@ tests/cases/conformance/expressions/functionCalls/functionCalls.ts(33,1): error
910
tests/cases/conformance/expressions/functionCalls/functionCalls.ts(34,1): error TS2347: Untyped function calls may not accept type arguments.
1011

1112

12-
==== tests/cases/conformance/expressions/functionCalls/functionCalls.ts (9 errors) ====
13+
==== tests/cases/conformance/expressions/functionCalls/functionCalls.ts (10 errors) ====
1314
// Invoke function call on value of type 'any' with no type arguments
1415
var anyVar: any;
1516
anyVar(0);
@@ -26,6 +27,8 @@ tests/cases/conformance/expressions/functionCalls/functionCalls.ts(34,1): error
2627
anyVar<Window>(undefined);
2728
~~~~~~~~~~~~~~~~~~~~~~~~~
2829
!!! error TS2347: Untyped function calls may not accept type arguments.
30+
~~~~~~
31+
!!! error TS2304: Cannot find name 'Window'.
2932

3033

3134
// Invoke function call on value of a subtype of Function with no call signatures with no type arguments
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts(1,1): error TS2304: Cannot find name 'f'.
22
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts(1,3): error TS2304: Cannot find name 'g'.
3+
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts(1,5): error TS2304: Cannot find name 'A'.
4+
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts(1,8): error TS2304: Cannot find name 'B'.
35

46

5-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts (2 errors) ====
7+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguity1.ts (4 errors) ====
68
f(g<A, B>(7));
79
~
810
!!! error TS2304: Cannot find name 'f'.
911
~
10-
!!! error TS2304: Cannot find name 'g'.
12+
!!! error TS2304: Cannot find name 'g'.
13+
~
14+
!!! error TS2304: Cannot find name 'A'.
15+
~
16+
!!! error TS2304: Cannot find name 'B'.
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguityWithBinaryOperator4.ts(3,9): error TS2347: Untyped function calls may not accept type arguments.
2+
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguityWithBinaryOperator4.ts(3,11): error TS2304: Cannot find name 'b'.
3+
tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguityWithBinaryOperator4.ts(3,14): error TS2304: Cannot find name 'b'.
24

35

4-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguityWithBinaryOperator4.ts (1 errors) ====
6+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserAmbiguityWithBinaryOperator4.ts (3 errors) ====
57
function g() {
68
var a, b, c;
79
if (a<b, b>(c + 1)) { }
810
~~~~~~~~~~~~~~
911
!!! error TS2347: Untyped function calls may not accept type arguments.
12+
~
13+
!!! error TS2304: Cannot find name 'b'.
14+
~
15+
!!! error TS2304: Cannot find name 'b'.
1016
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint4.ts(1,19): error TS2304: Cannot find name 'List'.
2+
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint4.ts(1,24): error TS2304: Cannot find name 'List'.
23

34

4-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint4.ts (1 errors) ====
5+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint4.ts (2 errors) ====
56
class C<T extends List<List<T> > > {
67
~~~~
8+
!!! error TS2304: Cannot find name 'List'.
9+
~~~~
710
!!! error TS2304: Cannot find name 'List'.
811
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint5.ts(1,19): error TS2304: Cannot find name 'List'.
2+
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint5.ts(1,24): error TS2304: Cannot find name 'List'.
23

34

4-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint5.ts (1 errors) ====
5+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint5.ts (2 errors) ====
56
class C<T extends List<List<T>> > {
67
~~~~
8+
!!! error TS2304: Cannot find name 'List'.
9+
~~~~
710
!!! error TS2304: Cannot find name 'List'.
811
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint6.ts(1,19): error TS2304: Cannot find name 'List'.
2+
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint6.ts(1,24): error TS2304: Cannot find name 'List'.
23

34

4-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint6.ts (1 errors) ====
5+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint6.ts (2 errors) ====
56
class C<T extends List<List<T> >> {
67
~~~~
8+
!!! error TS2304: Cannot find name 'List'.
9+
~~~~
710
!!! error TS2304: Cannot find name 'List'.
811
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint7.ts(1,19): error TS2304: Cannot find name 'List'.
2+
tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint7.ts(1,24): error TS2304: Cannot find name 'List'.
23

34

4-
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint7.ts (1 errors) ====
5+
==== tests/cases/conformance/parser/ecmascript5/Generics/parserGenericConstraint7.ts (2 errors) ====
56
class C<T extends List<List<T>>> {
67
~~~~
8+
!!! error TS2304: Cannot find name 'List'.
9+
~~~~
710
!!! error TS2304: Cannot find name 'List'.
811
}

0 commit comments

Comments
 (0)