Skip to content

Commit 8670937

Browse files
author
Andy Hanson
committed
Merge branch 'master' into map4
2 parents dec5f4b + aad663c commit 8670937

36 files changed

+552
-152
lines changed

src/compiler/checker.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ namespace ts {
10711071

10721072
if (moduleSymbol) {
10731073
let exportDefaultSymbol: Symbol;
1074-
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
1074+
if (isUntypedModuleSymbol(moduleSymbol)) {
10751075
exportDefaultSymbol = moduleSymbol;
10761076
}
10771077
else {
@@ -1151,7 +1151,7 @@ namespace ts {
11511151
if (targetSymbol) {
11521152
const name = specifier.propertyName || specifier.name;
11531153
if (name.text) {
1154-
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
1154+
if (isUntypedModuleSymbol(moduleSymbol)) {
11551155
return moduleSymbol;
11561156
}
11571157

@@ -1371,8 +1371,9 @@ namespace ts {
13711371
}
13721372

13731373
const isRelative = isExternalModuleNameRelative(moduleName);
1374+
const quotedName = '"' + moduleName + '"';
13741375
if (!isRelative) {
1375-
const symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
1376+
const symbol = getSymbol(globals, quotedName, SymbolFlags.ValueModule);
13761377
if (symbol) {
13771378
// merged symbol is module declaration symbol combined with all augmentations
13781379
return getMergedSymbol(symbol);
@@ -1401,6 +1402,28 @@ namespace ts {
14011402
}
14021403
}
14031404

1405+
// May be an untyped module. If so, ignore resolutionDiagnostic.
1406+
if (!isRelative && resolvedModule && !extensionIsTypeScript(resolvedModule.extension)) {
1407+
if (compilerOptions.noImplicitAny) {
1408+
if (moduleNotFoundError) {
1409+
error(errorNode,
1410+
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
1411+
moduleReference,
1412+
resolvedModule.resolvedFileName);
1413+
}
1414+
return undefined;
1415+
}
1416+
1417+
// Create a new symbol to represent the untyped module and store it in globals.
1418+
// This provides a name to the module. See the test tests/cases/fourslash/untypedModuleImport.ts
1419+
const newSymbol = createSymbol(SymbolFlags.ValueModule, quotedName);
1420+
// Module symbols are expected to have 'exports', although since this is an untyped module it can be empty.
1421+
newSymbol.exports = new StringMap<Symbol>();
1422+
// Cache it so subsequent accesses will return the same module.
1423+
globals.set(quotedName, newSymbol);
1424+
return newSymbol;
1425+
}
1426+
14041427
if (moduleNotFoundError) {
14051428
// report errors only if it was requested
14061429
if (resolutionDiagnostic) {
@@ -3470,7 +3493,7 @@ namespace ts {
34703493
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
34713494
const links = getSymbolLinks(symbol);
34723495
if (!links.type) {
3473-
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModuleSymbol(symbol)) {
3496+
if (symbol.flags & SymbolFlags.Module && isUntypedModuleSymbol(symbol)) {
34743497
links.type = anyType;
34753498
}
34763499
else {
@@ -19014,7 +19037,7 @@ namespace ts {
1901419037

1901519038
function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean {
1901619039
let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression);
19017-
if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) {
19040+
if (!moduleSymbol || isUntypedModuleSymbol(moduleSymbol)) {
1901819041
// If the module is not found or is shorthand, assume that it may export a value.
1901919042
return true;
1902019043
}
@@ -19514,7 +19537,7 @@ namespace ts {
1951419537
(typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective);
1951519538
}
1951619539
else {
19517-
// found at least one entry that does not originate from type reference directive
19540+
// found at least one entry that does not originate from type reference directive
1951819541
return undefined;
1951919542
}
1952019543
}

src/compiler/core.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ namespace ts {
9999
return undefined;
100100
}
101101

102+
export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
103+
Debug.assert(arrayA.length === arrayB.length);
104+
for (let i = 0; i < arrayA.length; i++) {
105+
callback(arrayA[i], arrayB[i], i);
106+
}
107+
}
108+
102109
/**
103110
* Iterates through `array` by index and performs the callback on each element of array until the callback
104111
* returns a falsey value, then returns false.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2901,6 +2901,10 @@
29012901
"category": "Error",
29022902
"code": 7015
29032903
},
2904+
"Could not find a declaration file for module '{0}'. '{1}' implicitly has an 'any' type.": {
2905+
"category": "Error",
2906+
"code": 7016
2907+
},
29042908
"Index signature of object type implicitly has an 'any' type.": {
29052909
"category": "Error",
29062910
"code": 7017

src/compiler/moduleNameResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace ts {
4545
}
4646

4747
/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
48-
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModule {
48+
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
4949
return { resolvedFileName: path, extension, isExternalLibraryImport };
5050
}
5151

src/compiler/program.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -329,16 +329,16 @@ namespace ts {
329329
// Map storing if there is emit blocking diagnostics for given input
330330
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
331331

332-
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModule[];
332+
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
333333
if (host.resolveModuleNames) {
334334
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
335335
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
336-
if (!resolved || resolved.extension !== undefined) {
337-
return resolved;
336+
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
337+
return resolved as ResolvedModuleFull;
338338
}
339-
resolved = clone(resolved);
340-
resolved.extension = extensionFromPath(resolved.resolvedFileName);
341-
return resolved;
339+
const withExtension = clone(resolved) as ResolvedModuleFull;
340+
withExtension.extension = extensionFromPath(resolved.resolvedFileName);
341+
return withExtension;
342342
});
343343
}
344344
else {
@@ -1290,7 +1290,7 @@ namespace ts {
12901290
function processImportedModules(file: SourceFile) {
12911291
collectExternalModuleReferences(file);
12921292
if (file.imports.length || file.moduleAugmentations.length) {
1293-
file.resolvedModules = new StringMap<ResolvedModule>();
1293+
file.resolvedModules = new StringMap<ResolvedModuleFull>();
12941294
const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral);
12951295
const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory));
12961296
Debug.assert(resolutions.length === moduleNames.length);
@@ -1317,6 +1317,7 @@ namespace ts {
13171317
// - it's not a top level JavaScript module that exceeded the search max
13181318
const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
13191319
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
1320+
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
13201321
const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport;
13211322

13221323
if (elideImport) {
@@ -1564,22 +1565,29 @@ namespace ts {
15641565

15651566
/* @internal */
15661567
/**
1567-
* Returns a DiagnosticMessage if we can't use a resolved module due to its extension.
1568+
* Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
15681569
* The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
1570+
* This returns a diagnostic even if the module will be an untyped module.
15691571
*/
1570-
export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModule): DiagnosticMessage | undefined {
1572+
export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
15711573
switch (extension) {
15721574
case Extension.Ts:
15731575
case Extension.Dts:
15741576
// These are always allowed.
15751577
return undefined;
1576-
15771578
case Extension.Tsx:
1579+
return needJsx();
15781580
case Extension.Jsx:
1579-
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
1580-
1581+
return needJsx() || needAllowJs();
15811582
case Extension.Js:
1582-
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
1583+
return needAllowJs();
1584+
}
1585+
1586+
function needJsx() {
1587+
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
1588+
}
1589+
function needAllowJs() {
1590+
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
15831591
}
15841592
}
15851593
}

src/compiler/types.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ namespace ts {
458458
ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node
459459
JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript
460460
ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error
461-
HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node
461+
HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node
462462

463463
BlockScoped = Let | Const,
464464

@@ -2118,7 +2118,7 @@ namespace ts {
21182118
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
21192119
// It is used to resolve module names in the checker.
21202120
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
2121-
/* @internal */ resolvedModules: Map<string, ResolvedModule>;
2121+
/* @internal */ resolvedModules: Map<string, ResolvedModuleFull>;
21222122
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<string, ResolvedTypeReferenceDirective>;
21232123
/* @internal */ imports: LiteralExpression[];
21242124
/* @internal */ moduleAugmentations: LiteralExpression[];
@@ -3387,14 +3387,11 @@ namespace ts {
33873387
* Module resolution will pick up tsx/jsx/js files even if '--jsx' and '--allowJs' are turned off.
33883388
* The Program will then filter results based on these flags.
33893389
*
3390-
* At least one of `resolvedTsFileName` or `resolvedJsFileName` must be defined,
3391-
* else resolution should just return `undefined` instead of a ResolvedModule.
3390+
* Prefer to return a `ResolvedModuleFull` so that the file type does not have to be inferred.
33923391
*/
33933392
export interface ResolvedModule {
33943393
/** Path of the file the module was resolved to. */
33953394
resolvedFileName: string;
3396-
/** Extension of resolvedFileName. This must match what's at the end of resolvedFileName. */
3397-
extension: Extension;
33983395
/**
33993396
* Denotes if 'resolvedFileName' is isExternalLibraryImport and thus should be a proper external module:
34003397
* - be a .d.ts file
@@ -3404,6 +3401,18 @@ namespace ts {
34043401
isExternalLibraryImport?: boolean;
34053402
}
34063403

3404+
/**
3405+
* ResolvedModule with an explicitly provided `extension` property.
3406+
* Prefer this over `ResolvedModule`.
3407+
*/
3408+
export interface ResolvedModuleFull extends ResolvedModule {
3409+
/**
3410+
* Extension of resolvedFileName. This must match what's at the end of resolvedFileName.
3411+
* This is optional for backwards-compatibility, but will be added if not provided.
3412+
*/
3413+
extension: Extension;
3414+
}
3415+
34073416
export enum Extension {
34083417
Ts,
34093418
Tsx,
@@ -3414,7 +3423,7 @@ namespace ts {
34143423
}
34153424

34163425
export interface ResolvedModuleWithFailedLookupLocations {
3417-
resolvedModule: ResolvedModule | undefined;
3426+
resolvedModule: ResolvedModuleFull | undefined;
34183427
failedLookupLocations: string[];
34193428
}
34203429

src/compiler/utilities.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ namespace ts {
8787
return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText));
8888
}
8989

90-
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule {
90+
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleFull {
9191
return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules.get(moduleNameText) : undefined;
9292
}
9393

94-
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void {
94+
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull): void {
9595
if (!sourceFile.resolvedModules) {
96-
sourceFile.resolvedModules = new StringMap<ResolvedModule>();
96+
sourceFile.resolvedModules = new StringMap<ResolvedModuleFull>();
9797
}
9898

9999
sourceFile.resolvedModules.set(moduleNameText, resolvedModule);
@@ -108,11 +108,7 @@ namespace ts {
108108
}
109109

110110
/* @internal */
111-
/**
112-
* Considers two ResolvedModules equal if they have the same `resolvedFileName`.
113-
* Thus `{ ts: foo, js: bar }` is equal to `{ ts: foo, js: baz }` because `ts` is preferred.
114-
*/
115-
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModule, newResolution: ResolvedModule): boolean {
111+
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
116112
return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
117113
oldResolution.extension === newResolution.extension &&
118114
oldResolution.resolvedFileName === newResolution.resolvedFileName;
@@ -406,8 +402,9 @@ namespace ts {
406402
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
407403
}
408404

409-
export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
410-
return isShorthandAmbientModule(moduleSymbol.valueDeclaration);
405+
/** Given a symbol for a module, checks that it is either an untyped import or a shorthand ambient module. */
406+
export function isUntypedModuleSymbol(moduleSymbol: Symbol): boolean {
407+
return !moduleSymbol.valueDeclaration || isShorthandAmbientModule(moduleSymbol.valueDeclaration);
411408
}
412409

413410
function isShorthandAmbientModule(node: Node): boolean {

0 commit comments

Comments
 (0)