Skip to content

Commit 3c6dc4e

Browse files
committed
Merge branch 'master' into jsx-element-realtypes
2 parents f74105a + 5ec5e04 commit 3c6dc4e

File tree

185 files changed

+1813
-695
lines changed

Some content is hidden

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

185 files changed

+1813
-695
lines changed

src/compiler/binder.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,8 +2499,13 @@ namespace ts {
24992499
declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true);
25002500
break;
25012501
case SyntaxKind.SourceFile:
2502-
// this.foo assignment in a source file
2503-
// Do not bind. It would be nice to support this someday though.
2502+
// this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script
2503+
if ((thisContainer as SourceFile).commonJsModuleIndicator) {
2504+
declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
2505+
}
2506+
else {
2507+
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
2508+
}
25042509
break;
25052510

25062511
default:

src/compiler/checker.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,16 @@ namespace ts {
8888
const emitResolver = createResolver();
8989
const nodeBuilder = createNodeBuilder();
9090

91+
const globals = createSymbolTable();
9192
const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String);
9293
undefinedSymbol.declarations = [];
94+
95+
const globalThisSymbol = createSymbol(SymbolFlags.Module, "globalThis" as __String, CheckFlags.Readonly);
96+
globalThisSymbol.exports = globals;
97+
globalThisSymbol.valueDeclaration = createNode(SyntaxKind.Identifier) as Identifier;
98+
(globalThisSymbol.valueDeclaration as Identifier).escapedText = "globalThis" as __String;
99+
globals.set(globalThisSymbol.escapedName, globalThisSymbol);
100+
93101
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String);
94102
const requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String);
95103

@@ -310,9 +318,9 @@ namespace ts {
310318
getAccessibleSymbolChain,
311319
getTypePredicateOfSignature: getTypePredicateOfSignature as (signature: Signature) => TypePredicate, // TODO: GH#18217
312320
resolveExternalModuleSymbol,
313-
tryGetThisTypeAt: node => {
321+
tryGetThisTypeAt: (node, includeGlobalThis) => {
314322
node = getParseTreeNode(node);
315-
return node && tryGetThisTypeAt(node);
323+
return node && tryGetThisTypeAt(node, includeGlobalThis);
316324
},
317325
getTypeArgumentConstraint: nodeIn => {
318326
const node = getParseTreeNode(nodeIn, isTypeNode);
@@ -459,7 +467,6 @@ namespace ts {
459467

460468
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
461469

462-
const globals = createSymbolTable();
463470
interface DuplicateInfoForSymbol {
464471
readonly firstFileLocations: Node[];
465472
readonly secondFileLocations: Node[];
@@ -9711,7 +9718,7 @@ namespace ts {
97119718
}
97129719

97139720
function getLiteralTypeFromProperties(type: Type, include: TypeFlags) {
9714-
return getUnionType(map(getPropertiesOfType(type), t => getLiteralTypeFromProperty(t, include)));
9721+
return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include)));
97159722
}
97169723

97179724
function getNonEnumNumberIndexInfo(type: Type) {
@@ -12830,20 +12837,30 @@ namespace ts {
1283012837
}
1283112838
return Ternary.False;
1283212839

12840+
function isNonGeneric(type: Type) {
12841+
// If we're already in identity relationship checking, we should use `isRelatedTo`
12842+
// to catch the `Maybe` from an excessively deep type (which we then assume means
12843+
// that the type could possibly contain a generic)
12844+
if (relation === identityRelation) {
12845+
return isRelatedTo(type, getPermissiveInstantiation(type)) === Ternary.True;
12846+
}
12847+
return isTypeIdenticalTo(type, getPermissiveInstantiation(type));
12848+
}
12849+
1283312850
function relateVariances(sourceTypeArguments: ReadonlyArray<Type> | undefined, targetTypeArguments: ReadonlyArray<Type> | undefined, variances: Variance[]) {
1283412851
if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) {
1283512852
return result;
1283612853
}
12837-
const isCovariantVoid = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances);
12838-
varianceCheckFailed = !isCovariantVoid;
12854+
const allowStructuralFallback = (targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances)) || isNonGeneric(source) || isNonGeneric(target);
12855+
varianceCheckFailed = !allowStructuralFallback;
1283912856
// The type arguments did not relate appropriately, but it may be because we have no variance
1284012857
// information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
1284112858
// arguments). It might also be the case that the target type has a 'void' type argument for
1284212859
// a covariant type parameter that is only used in return positions within the generic type
1284312860
// (in which case any type argument is permitted on the source side). In those cases we proceed
1284412861
// with a structural comparison. Otherwise, we know for certain the instantiations aren't
1284512862
// related and we can return here.
12846-
if (variances !== emptyArray && !isCovariantVoid) {
12863+
if (variances !== emptyArray && !allowStructuralFallback) {
1284712864
// In some cases generic types that are covariant in regular type checking mode become
1284812865
// invariant in --strictFunctionTypes mode because one or more type parameters are used in
1284912866
// both co- and contravariant positions. In order to make it easier to diagnose *why* such
@@ -16988,25 +17005,27 @@ namespace ts {
1698817005
captureLexicalThis(node, container);
1698917006
}
1699017007

16991-
const type = tryGetThisTypeAt(node, container);
16992-
if (!type && noImplicitThis) {
16993-
// With noImplicitThis, functions may not reference 'this' if it has type 'any'
16994-
const diag = error(
16995-
node,
16996-
capturedByArrowFunction && container.kind === SyntaxKind.SourceFile ?
16997-
Diagnostics.The_containing_arrow_function_captures_the_global_value_of_this_which_implicitly_has_type_any :
16998-
Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation);
16999-
if (!isSourceFile(container)) {
17000-
const outsideThis = tryGetThisTypeAt(container);
17001-
if (outsideThis) {
17002-
addRelatedInfo(diag, createDiagnosticForNode(container, Diagnostics.An_outer_value_of_this_is_shadowed_by_this_container));
17008+
const type = tryGetThisTypeAt(node, /*includeGlobalThis*/ true, container);
17009+
if (noImplicitThis) {
17010+
const globalThisType = getTypeOfSymbol(globalThisSymbol);
17011+
if (type === globalThisType && capturedByArrowFunction) {
17012+
error(node, Diagnostics.The_containing_arrow_function_captures_the_global_value_of_this);
17013+
}
17014+
else if (!type) {
17015+
// With noImplicitThis, functions may not reference 'this' if it has type 'any'
17016+
const diag = error(node, Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation);
17017+
if (!isSourceFile(container)) {
17018+
const outsideThis = tryGetThisTypeAt(container);
17019+
if (outsideThis && outsideThis !== globalThisType) {
17020+
addRelatedInfo(diag, createDiagnosticForNode(container, Diagnostics.An_outer_value_of_this_is_shadowed_by_this_container));
17021+
}
1700317022
}
1700417023
}
1700517024
}
1700617025
return type || anyType;
1700717026
}
1700817027

17009-
function tryGetThisTypeAt(node: Node, container = getThisContainer(node, /*includeArrowFunctions*/ false)): Type | undefined {
17028+
function tryGetThisTypeAt(node: Node, includeGlobalThis = true, container = getThisContainer(node, /*includeArrowFunctions*/ false)): Type | undefined {
1701017029
const isInJS = isInJSFile(node);
1701117030
if (isFunctionLike(container) &&
1701217031
(!isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container))) {
@@ -17053,6 +17072,16 @@ namespace ts {
1705317072
return getFlowTypeOfReference(node, type);
1705417073
}
1705517074
}
17075+
if (isSourceFile(container)) {
17076+
// look up in the source file's locals or exports
17077+
if (container.commonJsModuleIndicator) {
17078+
const fileSymbol = getSymbolOfNode(container);
17079+
return fileSymbol && getTypeOfSymbol(fileSymbol);
17080+
}
17081+
else if (includeGlobalThis) {
17082+
return getTypeOfSymbol(globalThisSymbol);
17083+
}
17084+
}
1705617085
}
1705717086

1705817087
function getClassNameFromPrototypeMethod(container: Node) {
@@ -19393,6 +19422,12 @@ namespace ts {
1939319422
if (isJSLiteralType(leftType)) {
1939419423
return anyType;
1939519424
}
19425+
if (leftType.symbol === globalThisSymbol) {
19426+
if (noImplicitAny) {
19427+
error(right, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(leftType));
19428+
}
19429+
return anyType;
19430+
}
1939619431
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
1939719432
reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType);
1939819433
}

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4149,7 +4149,7 @@
41494149
"category": "Error",
41504150
"code": 7040
41514151
},
4152-
"The containing arrow function captures the global value of 'this' which implicitly has type 'any'.": {
4152+
"The containing arrow function captures the global value of 'this'.": {
41534153
"category": "Error",
41544154
"code": 7041
41554155
},

src/compiler/sys.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,17 @@ namespace ts {
615615
directoryExists,
616616
createDirectory(directoryName: string) {
617617
if (!nodeSystem.directoryExists(directoryName)) {
618-
_fs.mkdirSync(directoryName);
618+
// Wrapped in a try-catch to prevent crashing if we are in a race
619+
// with another copy of ourselves to create the same directory
620+
try {
621+
_fs.mkdirSync(directoryName);
622+
}
623+
catch (e) {
624+
if (e.code !== "EEXIST") {
625+
// Failed for some other reason (access denied?); still throw
626+
throw e;
627+
}
628+
}
619629
}
620630
},
621631
getExecutingFilePath() {

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3223,7 +3223,7 @@ namespace ts {
32233223
*/
32243224
/* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol;
32253225
/** @param node A location where we might consider accessing `this`. Not necessarily a ThisExpression. */
3226-
/* @internal */ tryGetThisTypeAt(node: Node): Type | undefined;
3226+
/* @internal */ tryGetThisTypeAt(node: Node, includeGlobalThis?: boolean): Type | undefined;
32273227
/* @internal */ getTypeArgumentConstraint(node: TypeNode): Type | undefined;
32283228

32293229
/**

src/harness/fourslash.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ namespace FourSlash {
774774
if ("exact" in options) {
775775
ts.Debug.assert(!("includes" in options) && !("excludes" in options));
776776
if (options.exact === undefined) throw this.raiseError("Expected no completions");
777-
this.verifyCompletionsAreExactly(actualCompletions.entries, toArray(options.exact));
777+
this.verifyCompletionsAreExactly(actualCompletions.entries, toArray(options.exact), options.marker);
778778
}
779779
else {
780780
if (options.includes) {
@@ -841,14 +841,14 @@ namespace FourSlash {
841841
}
842842
}
843843

844-
private verifyCompletionsAreExactly(actual: ReadonlyArray<ts.CompletionEntry>, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>) {
844+
private verifyCompletionsAreExactly(actual: ReadonlyArray<ts.CompletionEntry>, expected: ReadonlyArray<FourSlashInterface.ExpectedCompletionEntry>, marker?: ArrayOrSingle<string | Marker>) {
845845
// First pass: test that names are right. Then we'll test details.
846-
assert.deepEqual(actual.map(a => a.name), expected.map(e => typeof e === "string" ? e : e.name));
846+
assert.deepEqual(actual.map(a => a.name), expected.map(e => typeof e === "string" ? e : e.name), marker ? "At marker " + JSON.stringify(marker) : undefined);
847847

848848
ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
849849
const name = typeof expectedCompletion === "string" ? expectedCompletion : expectedCompletion.name;
850850
if (completion.name !== name) {
851-
this.raiseError(`Expected completion at index ${index} to be ${name}, got ${completion.name}`);
851+
this.raiseError(`${marker ? JSON.stringify(marker) : "" } Expected completion at index ${index} to be ${name}, got ${completion.name}`);
852852
}
853853
this.verifyCompletionEntry(completion, expectedCompletion);
854854
});
@@ -4545,6 +4545,7 @@ namespace FourSlashInterface {
45454545

45464546
export function globalTypesPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
45474547
return [
4548+
{ name: "globalThis", kind: "module" },
45484549
...globalTypeDecls,
45494550
...plus,
45504551
...typeKeywords,
@@ -4786,6 +4787,7 @@ namespace FourSlashInterface {
47864787
export const globalsInsideFunction = (plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> => [
47874788
{ name: "arguments", kind: "local var" },
47884789
...plus,
4790+
{ name: "globalThis", kind: "module" },
47894791
...globalsVars,
47904792
{ name: "undefined", kind: "var" },
47914793
...globalKeywordsInsideFunction,
@@ -4921,13 +4923,19 @@ namespace FourSlashInterface {
49214923
})();
49224924

49234925
export const globals: ReadonlyArray<ExpectedCompletionEntryObject> = [
4926+
{ name: "globalThis", kind: "module" },
49244927
...globalsVars,
49254928
{ name: "undefined", kind: "var" },
49264929
...globalKeywords
49274930
];
49284931

49294932
export function globalsPlus(plus: ReadonlyArray<ExpectedCompletionEntry>): ReadonlyArray<ExpectedCompletionEntry> {
4930-
return [...globalsVars, ...plus, { name: "undefined", kind: "var" }, ...globalKeywords];
4933+
return [
4934+
{ name: "globalThis", kind: "module" },
4935+
...globalsVars,
4936+
...plus,
4937+
{ name: "undefined", kind: "var" },
4938+
...globalKeywords];
49314939
}
49324940
}
49334941

src/services/completions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ namespace ts.Completions {
10301030

10311031
// Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
10321032
if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) {
1033-
const thisType = typeChecker.tryGetThisTypeAt(scopeNode);
1033+
const thisType = typeChecker.tryGetThisTypeAt(scopeNode, /*includeGlobalThis*/ false);
10341034
if (thisType) {
10351035
for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
10361036
symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType };

src/services/symbolDisplay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ namespace ts.SymbolDisplay {
310310
displayParts.push(spacePart());
311311
addFullSymbolName(symbol);
312312
}
313-
if (symbolFlags & SymbolFlags.Module) {
313+
if (symbolFlags & SymbolFlags.Module && !isThisExpression) {
314314
prefixNextMeaning();
315315
const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration);
316316
const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier;

src/testRunner/unittests/tsserver/projects.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,8 @@ namespace ts.projectSystem {
708708
// Check identifiers defined in HTML content are available in .ts file
709709
const project = configuredProjectAt(projectService, 0);
710710
let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, emptyOptions);
711-
assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`);
711+
assert(completions && completions.entries[1].name === "hello", `expected entry hello to be in completion list`);
712+
assert(completions && completions.entries[0].name === "globalThis", `first entry should be globalThis (not strictly relevant for this test).`);
712713

713714
// Close HTML file
714715
projectService.applyChangesInOpenFiles(

tests/baselines/reference/assignmentLHSIsValue.symbols

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function foo() { this = value; }
2727
>value : Symbol(value, Decl(assignmentLHSIsValue.ts, 1, 3))
2828

2929
this = value;
30+
>this : Symbol(globalThis)
3031
>value : Symbol(value, Decl(assignmentLHSIsValue.ts, 1, 3))
3132

3233
// identifiers: module, class, enum, function
@@ -116,6 +117,7 @@ foo() = value;
116117

117118
// parentheses, the containted expression is value
118119
(this) = value;
120+
>this : Symbol(globalThis)
119121
>value : Symbol(value, Decl(assignmentLHSIsValue.ts, 1, 3))
120122

121123
(M) = value;

0 commit comments

Comments
 (0)