Skip to content

Commit a72cf5c

Browse files
authored
Merge pull request #24329 from Microsoft/MergeMaster5-22-2
Merge master 5/22
2 parents 8995126 + 4f25e83 commit a72cf5c

File tree

134 files changed

+1256
-943
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

134 files changed

+1256
-943
lines changed

src/compiler/binder.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ namespace ts {
158158
* If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
159159
* This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
160160
*/
161-
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
161+
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
162162
return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
163163
}
164164

@@ -1208,7 +1208,7 @@ namespace ts {
12081208
bind(node.statement);
12091209
popActiveLabel();
12101210
if (!activeLabel.referenced && !options.allowUnusedLabels) {
1211-
file.bindDiagnostics.push(createDiagnosticForNode(node.label, Diagnostics.Unused_label));
1211+
errorOrSuggestionOnFirstToken(unusedLabelIsError(options), node, Diagnostics.Unused_label);
12121212
}
12131213
if (!node.statement || node.statement.kind !== SyntaxKind.DoStatement) {
12141214
// do statement sets current flow inside bindDoStatement
@@ -1914,6 +1914,17 @@ namespace ts {
19141914
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
19151915
}
19161916

1917+
function errorOrSuggestionOnFirstToken(isError: boolean, node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
1918+
const span = getSpanOfTokenAtPosition(file, node.pos);
1919+
const diag = createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2);
1920+
if (isError) {
1921+
file.bindDiagnostics.push(diag);
1922+
}
1923+
else {
1924+
file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
1925+
}
1926+
}
1927+
19171928
function bind(node: Node): void {
19181929
if (!node) {
19191930
return;
@@ -2730,26 +2741,26 @@ namespace ts {
27302741
if (reportError) {
27312742
currentFlow = reportedUnreachableFlow;
27322743

2733-
// unreachable code is reported if
2734-
// - user has explicitly asked about it AND
2735-
// - statement is in not ambient context (statements in ambient context is already an error
2736-
// so we should not report extras) AND
2737-
// - node is not variable statement OR
2738-
// - node is block scoped variable statement OR
2739-
// - node is not block scoped variable statement and at least one variable declaration has initializer
2740-
// Rationale: we don't want to report errors on non-initialized var's since they are hoisted
2741-
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
2742-
const reportUnreachableCode =
2743-
!options.allowUnreachableCode &&
2744-
!(node.flags & NodeFlags.Ambient) &&
2745-
(
2746-
node.kind !== SyntaxKind.VariableStatement ||
2747-
getCombinedNodeFlags((<VariableStatement>node).declarationList) & NodeFlags.BlockScoped ||
2748-
forEach((<VariableStatement>node).declarationList.declarations, d => d.initializer)
2749-
);
2750-
2751-
if (reportUnreachableCode) {
2752-
errorOnFirstToken(node, Diagnostics.Unreachable_code_detected);
2744+
if (!options.allowUnreachableCode) {
2745+
// unreachable code is reported if
2746+
// - user has explicitly asked about it AND
2747+
// - statement is in not ambient context (statements in ambient context is already an error
2748+
// so we should not report extras) AND
2749+
// - node is not variable statement OR
2750+
// - node is block scoped variable statement OR
2751+
// - node is not block scoped variable statement and at least one variable declaration has initializer
2752+
// Rationale: we don't want to report errors on non-initialized var's since they are hoisted
2753+
// On the other side we do want to report errors on non-initialized 'lets' because of TDZ
2754+
const isError =
2755+
unreachableCodeIsError(options) &&
2756+
!(node.flags & NodeFlags.Ambient) &&
2757+
(
2758+
!isVariableStatement(node) ||
2759+
!!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
2760+
node.declarationList.declarations.some(d => !!d.initializer)
2761+
);
2762+
2763+
errorOrSuggestionOnFirstToken(isError, node, Diagnostics.Unreachable_code_detected);
27532764
}
27542765
}
27552766
}

src/compiler/checker.ts

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,11 @@ namespace ts {
313313

314314
getSuggestionDiagnostics: file => {
315315
return (suggestionDiagnostics.get(file.fileName) || emptyArray).concat(getUnusedDiagnostics());
316-
function getUnusedDiagnostics(): ReadonlyArray<Diagnostic> {
316+
function getUnusedDiagnostics(): ReadonlyArray<DiagnosticWithLocation> {
317317
if (file.isDeclarationFile) return emptyArray;
318318

319319
checkSourceFile(file);
320-
const diagnostics: Diagnostic[] = [];
320+
const diagnostics: DiagnosticWithLocation[] = [];
321321
Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
322322
checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (kind, diag) => {
323323
if (!unusedIsError(kind)) {
@@ -481,7 +481,7 @@ namespace ts {
481481

482482
const diagnostics = createDiagnosticCollection();
483483
// Suggestion diagnostics must have a file. Keyed by source file name.
484-
const suggestionDiagnostics = createMultiMap<Diagnostic>();
484+
const suggestionDiagnostics = createMultiMap<DiagnosticWithLocation>();
485485

486486
const enum TypeFacts {
487487
None = 0,
@@ -628,7 +628,7 @@ namespace ts {
628628
Local,
629629
Parameter,
630630
}
631-
type AddUnusedDiagnostic = (type: UnusedKind, diagnostic: Diagnostic) => void;
631+
type AddUnusedDiagnostic = (type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;
632632

633633
const builtinGlobals = createSymbolTable();
634634
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
@@ -824,7 +824,7 @@ namespace ts {
824824
diagnostics.add(diagnostic);
825825
}
826826

827-
function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) {
827+
function addErrorOrSuggestion(isError: boolean, diagnostic: DiagnosticWithLocation) {
828828
if (isError) {
829829
diagnostics.add(diagnostic);
830830
}
@@ -9286,8 +9286,10 @@ namespace ts {
92869286
return type;
92879287
}
92889288

9289-
function getRegularTypeOfLiteralType(type: Type) {
9290-
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : type;
9289+
function getRegularTypeOfLiteralType(type: Type): Type {
9290+
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType :
9291+
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) :
9292+
type;
92919293
}
92929294

92939295
function getLiteralType(value: string | number, enumId?: number, symbol?: Symbol) {
@@ -12774,10 +12776,12 @@ namespace ts {
1277412776
// all inferences were made to top-level occurrences of the type parameter, and
1277512777
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
1277612778
// the type parameter was fixed during inference or does not occur at top-level in the return type.
12777-
const widenLiteralTypes = inference.topLevel &&
12778-
!hasPrimitiveConstraint(inference.typeParameter) &&
12779+
const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter);
12780+
const widenLiteralTypes = !primitiveConstraint && inference.topLevel &&
1277912781
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
12780-
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
12782+
const baseCandidates = primitiveConstraint ? sameMap(candidates, getRegularTypeOfLiteralType) :
12783+
widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) :
12784+
candidates;
1278112785
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
1278212786
// union types were requested or if all inferences were made from the return type position, infer a
1278312787
// union type. Otherwise, infer a common supertype.
@@ -15303,6 +15307,26 @@ namespace ts {
1530315307
}
1530415308
}
1530515309

15310+
// Return true if the given expression is possibly a discriminant value. We limit the kinds of
15311+
// expressions we check to those that don't depend on their contextual type in order not to cause
15312+
// recursive (and possibly infinite) invocations of getContextualType.
15313+
function isPossiblyDiscriminantValue(node: Expression): boolean {
15314+
switch (node.kind) {
15315+
case SyntaxKind.StringLiteral:
15316+
case SyntaxKind.NumericLiteral:
15317+
case SyntaxKind.NoSubstitutionTemplateLiteral:
15318+
case SyntaxKind.TrueKeyword:
15319+
case SyntaxKind.FalseKeyword:
15320+
case SyntaxKind.NullKeyword:
15321+
case SyntaxKind.Identifier:
15322+
return true;
15323+
case SyntaxKind.PropertyAccessExpression:
15324+
case SyntaxKind.ParenthesizedExpression:
15325+
return isPossiblyDiscriminantValue((<PropertyAccessExpression | ParenthesizedExpression>node).expression);
15326+
}
15327+
return false;
15328+
}
15329+
1530615330
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
1530715331
// be "pushed" onto a node using the contextualType property.
1530815332
function getApparentTypeOfContextualType(node: Expression): Type {
@@ -15316,8 +15340,8 @@ namespace ts {
1531615340
propLoop: for (const prop of node.properties) {
1531715341
if (!prop.symbol) continue;
1531815342
if (prop.kind !== SyntaxKind.PropertyAssignment) continue;
15319-
if (isDiscriminantProperty(contextualType, prop.symbol.escapedName)) {
15320-
const discriminatingType = getTypeOfNode(prop.initializer);
15343+
if (isPossiblyDiscriminantValue(prop.initializer) && isDiscriminantProperty(contextualType, prop.symbol.escapedName)) {
15344+
const discriminatingType = checkExpression(prop.initializer);
1532115345
for (const type of (contextualType as UnionType).types) {
1532215346
const targetType = getTypeOfPropertyOfType(type, prop.symbol.escapedName);
1532315347
if (targetType && checkTypeAssignableTo(discriminatingType, targetType, /*errorNode*/ undefined)) {

src/compiler/core.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace ts {
1414
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
1515
}
1616

17-
export function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray<Diagnostic>): Diagnostic[] {
18-
return sortAndDeduplicate(diagnostics, compareDiagnostics);
17+
export function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: ReadonlyArray<T>): T[] {
18+
return sortAndDeduplicate<T>(diagnostics, compareDiagnostics);
1919
}
2020
}
2121

@@ -1619,8 +1619,8 @@ namespace ts {
16191619
return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message;
16201620
}
16211621

1622-
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number)[]): Diagnostic;
1623-
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
1622+
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number)[]): DiagnosticWithLocation;
1623+
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation {
16241624
Debug.assertGreaterThanOrEqual(start, 0);
16251625
Debug.assertGreaterThanOrEqual(length, 0);
16261626

@@ -1991,6 +1991,14 @@ namespace ts {
19911991
return moduleResolution;
19921992
}
19931993

1994+
export function unreachableCodeIsError(options: CompilerOptions): boolean {
1995+
return options.allowUnreachableCode === false;
1996+
}
1997+
1998+
export function unusedLabelIsError(options: CompilerOptions): boolean {
1999+
return options.allowUnusedLabels === false;
2000+
}
2001+
19942002
export function getAreDeclarationMapsEnabled(options: CompilerOptions) {
19952003
return !!(options.declaration && options.declarationMap);
19962004
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,7 +2238,8 @@
22382238
},
22392239
"Left side of comma operator is unused and has no side effects.": {
22402240
"category": "Error",
2241-
"code": 2695
2241+
"code": 2695,
2242+
"reportsUnnecessary": true
22422243
},
22432244
"The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?": {
22442245
"category": "Error",
@@ -3673,7 +3674,8 @@
36733674
},
36743675
"Unreachable code detected.": {
36753676
"category": "Error",
3676-
"code": 7027
3677+
"code": 7027,
3678+
"reportsUnnecessary": true
36773679
},
36783680
"Unused label.": {
36793681
"category": "Error",

src/compiler/factory.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,6 +2463,7 @@ namespace ts {
24632463
if (node.symbolCount !== undefined) updated.symbolCount = node.symbolCount;
24642464
if (node.parseDiagnostics !== undefined) updated.parseDiagnostics = node.parseDiagnostics;
24652465
if (node.bindDiagnostics !== undefined) updated.bindDiagnostics = node.bindDiagnostics;
2466+
if (node.bindSuggestionDiagnostics !== undefined) updated.bindSuggestionDiagnostics = node.bindSuggestionDiagnostics;
24662467
if (node.lineMap !== undefined) updated.lineMap = node.lineMap;
24672468
if (node.classifiableNames !== undefined) updated.classifiableNames = node.classifiableNames;
24682469
if (node.resolvedModules !== undefined) updated.resolvedModules = node.resolvedModules;

src/compiler/parser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ namespace ts {
595595
// tslint:enable variable-name
596596

597597
let sourceFile: SourceFile;
598-
let parseDiagnostics: Diagnostic[];
598+
let parseDiagnostics: DiagnosticWithLocation[];
599599
let syntaxCursor: IncrementalParser.SyntaxCursor;
600600

601601
let currentToken: SyntaxKind;
@@ -912,6 +912,7 @@ namespace ts {
912912

913913
sourceFile.text = sourceText;
914914
sourceFile.bindDiagnostics = [];
915+
sourceFile.bindSuggestionDiagnostics = undefined;
915916
sourceFile.languageVersion = languageVersion;
916917
sourceFile.fileName = normalizePath(fileName);
917918
sourceFile.languageVariant = getLanguageVariant(scriptKind);

0 commit comments

Comments
 (0)