Skip to content

Commit

Permalink
Merge branch 'master' into map4
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Hanson committed Oct 27, 2016
2 parents dec5f4b + aad663c commit 6c99097
Show file tree
Hide file tree
Showing 36 changed files with 552 additions and 152 deletions.
35 changes: 29 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ namespace ts {

if (moduleSymbol) {
let exportDefaultSymbol: Symbol;
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
if (isUntypedModuleSymbol(moduleSymbol)) {
exportDefaultSymbol = moduleSymbol;
}
else {
Expand Down Expand Up @@ -1151,7 +1151,7 @@ namespace ts {
if (targetSymbol) {
const name = specifier.propertyName || specifier.name;
if (name.text) {
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
if (isUntypedModuleSymbol(moduleSymbol)) {
return moduleSymbol;
}

Expand Down Expand Up @@ -1371,8 +1371,9 @@ namespace ts {
}

const isRelative = isExternalModuleNameRelative(moduleName);
const quotedName = '"' + moduleName + '"';
if (!isRelative) {
const symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
const symbol = getSymbol(globals, quotedName, SymbolFlags.ValueModule);
if (symbol) {
// merged symbol is module declaration symbol combined with all augmentations
return getMergedSymbol(symbol);
Expand Down Expand Up @@ -1401,6 +1402,28 @@ namespace ts {
}
}

// May be an untyped module. If so, ignore resolutionDiagnostic.
if (!isRelative && resolvedModule && !extensionIsTypeScript(resolvedModule.extension)) {
if (compilerOptions.noImplicitAny) {
if (moduleNotFoundError) {
error(errorNode,
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
moduleReference,
resolvedModule.resolvedFileName);
}
return undefined;
}

// Create a new symbol to represent the untyped module and store it in globals.
// This provides a name to the module. See the test tests/cases/fourslash/untypedModuleImport.ts
const newSymbol = createSymbol(SymbolFlags.ValueModule, quotedName);
// Module symbols are expected to have 'exports', although since this is an untyped module it can be empty.
newSymbol.exports = createMap<Symbol>();
// Cache it so subsequent accesses will return the same module.
globals[quotedName] = newSymbol;
return newSymbol;
}

if (moduleNotFoundError) {
// report errors only if it was requested
if (resolutionDiagnostic) {
Expand Down Expand Up @@ -3470,7 +3493,7 @@ namespace ts {
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModuleSymbol(symbol)) {
if (symbol.flags & SymbolFlags.Module && isUntypedModuleSymbol(symbol)) {
links.type = anyType;
}
else {
Expand Down Expand Up @@ -19014,7 +19037,7 @@ namespace ts {

function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean {
let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression);
if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) {
if (!moduleSymbol || isUntypedModuleSymbol(moduleSymbol)) {
// If the module is not found or is shorthand, assume that it may export a value.
return true;
}
Expand Down Expand Up @@ -19514,7 +19537,7 @@ namespace ts {
(typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective);
}
else {
// found at least one entry that does not originate from type reference directive
// found at least one entry that does not originate from type reference directive
return undefined;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ namespace ts {
return undefined;
}

export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
Debug.assert(arrayA.length === arrayB.length);
for (let i = 0; i < arrayA.length; i++) {
callback(arrayA[i], arrayB[i], i);
}
}

/**
* Iterates through `array` by index and performs the callback on each element of array until the callback
* returns a falsey value, then returns false.
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2901,6 +2901,10 @@
"category": "Error",
"code": 7015
},
"Could not find a declaration file for module '{0}'. '{1}' implicitly has an 'any' type.": {
"category": "Error",
"code": 7016
},
"Index signature of object type implicitly has an 'any' type.": {
"category": "Error",
"code": 7017
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace ts {
}

/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModule {
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
return { resolvedFileName: path, extension, isExternalLibraryImport };
}

Expand Down
34 changes: 21 additions & 13 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,16 +329,16 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);

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

if (elideImport) {
Expand Down Expand Up @@ -1564,22 +1565,29 @@ namespace ts {

/* @internal */
/**
* Returns a DiagnosticMessage if we can't use a resolved module due to its extension.
* Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
* The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
* This returns a diagnostic even if the module will be an untyped module.
*/
export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModule): DiagnosticMessage | undefined {
export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
switch (extension) {
case Extension.Ts:
case Extension.Dts:
// These are always allowed.
return undefined;

case Extension.Tsx:
return needJsx();
case Extension.Jsx:
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;

return needJsx() || needAllowJs();
case Extension.Js:
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
return needAllowJs();
}

function needJsx() {
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
}
function needAllowJs() {
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
}
}
}
23 changes: 16 additions & 7 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ namespace ts {
ThisNodeHasError = 1 << 19, // If the parser encountered an error when parsing the code that created this node
JavaScriptFile = 1 << 20, // If node was parsed in a JavaScript
ThisNodeOrAnySubNodesHasError = 1 << 21, // If this node or any of its children had an error
HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node
HasAggregatedChildData = 1 << 22, // If we've computed data from children and cached it in this node

BlockScoped = Let | Const,

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

/**
* ResolvedModule with an explicitly provided `extension` property.
* Prefer this over `ResolvedModule`.
*/
export interface ResolvedModuleFull extends ResolvedModule {
/**
* Extension of resolvedFileName. This must match what's at the end of resolvedFileName.
* This is optional for backwards-compatibility, but will be added if not provided.
*/
extension: Extension;
}

export enum Extension {
Ts,
Tsx,
Expand All @@ -3414,7 +3423,7 @@ namespace ts {
}

export interface ResolvedModuleWithFailedLookupLocations {
resolvedModule: ResolvedModule | undefined;
resolvedModule: ResolvedModuleFull | undefined;
failedLookupLocations: string[];
}

Expand Down
17 changes: 7 additions & 10 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ namespace ts {
return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText));
}

export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule {
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleFull {
return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules.get(moduleNameText) : undefined;
}

export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void {
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull): void {
if (!sourceFile.resolvedModules) {
sourceFile.resolvedModules = new StringMap<ResolvedModule>();
sourceFile.resolvedModules = new StringMap<ResolvedModuleFull>();
}

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

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

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

function isShorthandAmbientModule(node: Node): boolean {
Expand Down
Loading

0 comments on commit 6c99097

Please sign in to comment.