From 3b1982589007e0419c025ad716ec47e7ff2566cf Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 2 Jun 2016 05:43:40 -0700 Subject: [PATCH 1/5] Allow wildcard ("*") patterns in ambient module declarations --- src/compiler/binder.ts | 21 +++- src/compiler/checker.ts | 26 +++++ src/compiler/program.ts | 110 ++++++++++++------ src/compiler/types.ts | 13 +++ .../ambientDeclarationsPatterns.errors.txt | 25 ++++ .../reference/ambientDeclarationsPatterns.js | 28 +++++ .../ambient/ambientDeclarationsPatterns.ts | 19 +++ 7 files changed, 207 insertions(+), 35 deletions(-) create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.js create mode 100644 tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5fdfc7f02be42..d3363285afcdd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1287,7 +1287,26 @@ namespace ts { declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + let pattern: Pattern | undefined; + if (node.name.kind === SyntaxKind.StringLiteral) { + const text = (node.name).text; + if (hasZeroOrOneAsteriskCharacter(text)) { + pattern = tryParsePattern(text); + } + else { + errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text); + } + } + + if (pattern) { + // TODO: don't really need such a symbol in container.locals... + const symbol = declareSymbol(container.locals, undefined, node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + file.patternAmbientModules = file.patternAmbientModules || []; + file.patternAmbientModules.push({ pattern, symbol }); + } + else { + declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + } } } else { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06ef246340252..3e66cf1661f52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -140,6 +140,12 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); const globals: SymbolTable = {}; + /** + * List of every ambient module with a "*" wildcard. + * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. + * This is only used if there is no exact match. + */ + let patternAmbientModules: PatternAmbientModule[]; let getGlobalESSymbolConstructorSymbol: () => Symbol; @@ -1285,6 +1291,12 @@ namespace ts { } return undefined; } + + const patternModuleSymbol = getPatternAmbientModule(moduleName); + if (patternModuleSymbol) { + return getMergedSymbol(patternModuleSymbol); + } + if (moduleNotFoundError) { // report errors only if it was requested error(moduleReferenceLiteral, moduleNotFoundError, moduleName); @@ -1292,6 +1304,16 @@ namespace ts { return undefined; } + /** Get an ambient module with a wildcard ("*") in it. */ + function getPatternAmbientModule(name: string): Symbol | undefined { + if (patternAmbientModules) { + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, name); + if (pattern) { + return pattern.symbol; + } + } + } + // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { @@ -17627,6 +17649,10 @@ namespace ts { if (!isExternalOrCommonJsModule(file)) { mergeSymbolTable(globals, file.locals); } + if (file.patternAmbientModules && file.patternAmbientModules.length) { + (patternAmbientModules || (patternAmbientModules = [])).push(...file.patternAmbientModules); + } + if (file.moduleAugmentations.length) { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bab554927e726..1677f61c15df8 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -95,7 +95,7 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } - function hasZeroOrOneAsteriskCharacter(str: string): boolean { + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) === CharacterCodes.asterisk) { @@ -496,48 +496,23 @@ namespace ts { trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName); } - let longestMatchPrefixLength = -1; - let matchedPattern: string; - let matchedStar: string; - + // string is for exact match + let matchedPattern: Pattern | string | undefined = undefined; if (state.compilerOptions.paths) { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - - for (const key in state.compilerOptions.paths) { - const pattern: string = key; - const indexOfStar = pattern.indexOf("*"); - if (indexOfStar !== -1) { - const prefix = pattern.substr(0, indexOfStar); - const suffix = pattern.substr(indexOfStar + 1); - if (moduleName.length >= prefix.length + suffix.length && - startsWith(moduleName, prefix) && - endsWith(moduleName, suffix)) { - - // use length of prefix as betterness criteria - if (prefix.length > longestMatchPrefixLength) { - longestMatchPrefixLength = prefix.length; - matchedPattern = pattern; - matchedStar = moduleName.substr(prefix.length, moduleName.length - suffix.length); - } - } - } - else if (pattern === moduleName) { - // pattern was matched as is - no need to search further - matchedPattern = pattern; - matchedStar = undefined; - break; - } - } + matchedPattern = matchPatternOrExact(Object.keys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { + const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName); + const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern); if (state.traceEnabled) { - trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPattern); + trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } - for (const subst of state.compilerOptions.paths[matchedPattern]) { - const path = matchedStar ? subst.replace("\*", matchedStar) : subst; + for (const subst of state.compilerOptions.paths[matchedPatternText]) { + const path = matchedStar ? subst.replace("*", matchedStar) : subst; const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path)); if (state.traceEnabled) { trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); @@ -560,6 +535,73 @@ namespace ts { } } + /** + * patternStrings contains both pattern strings (containing "*") and regular strings. + * Return an exact match if possible, or a pattern match, or undefined. + * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) + */ + function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined { + const patterns: Pattern[] = []; + for (const patternString of patternStrings) { + const pattern = tryParsePattern(patternString); + if (pattern) { + patterns.push(pattern); + } + else if (patternString === candidate) { + // pattern was matched as is - no need to search further + return patternString; + } + } + + return findBestPatternMatch(patterns, _ => _, candidate); + } + + function patternText({prefix, suffix}: Pattern): string { + return `${prefix}*${suffix}`; + } + + /** + * Given that candidate matches pattern, returns the text matching the '*'. + * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar" + */ + function matchedText(pattern: Pattern, candidate: string): string { + Debug.assert(isPatternMatch(pattern, candidate)); + return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length); + } + + /** Return the object corresponding to the best pattern to match `candidate`. */ + export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { + let matchedValue: T | undefined = undefined; + // use length of prefix as betterness criteria + let longestMatchPrefixLength = -1; + + for (const v of values) { + const pattern = getPattern(v); + if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { + longestMatchPrefixLength = pattern.prefix.length; + matchedValue = v; + } + } + + return matchedValue; + } + + function isPatternMatch({prefix, suffix}: Pattern, candidate: string) { + return candidate.length >= prefix.length + suffix.length && + startsWith(candidate, prefix) && + endsWith(candidate, suffix); + } + + export function tryParsePattern(pattern: string): Pattern | undefined { + // This should be verified outside of here and a proper error thrown. + Debug.assert(hasZeroOrOneAsteriskCharacter(pattern)); + const indexOfStar = pattern.indexOf("*"); + return indexOfStar === -1 ? undefined : { + prefix: pattern.substr(0, indexOfStar), + suffix: pattern.substr(indexOfStar + 1) + }; + } + export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { const containingDirectory = getDirectoryPath(containingFile); const supportedExtensions = getSupportedExtensions(compilerOptions); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c7e14a1003108..a6f19f121cf01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1658,6 +1658,7 @@ namespace ts { /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; + /* @internal */ patternAmbientModules?: PatternAmbientModule[]; } export interface ScriptReferenceHost { @@ -2135,6 +2136,18 @@ namespace ts { [index: string]: Symbol; } + /** Represents a "prefix*suffix" pattern. */ + export interface Pattern { + prefix: string; + suffix: string; + } + + /** Used to track a `declare module "foo*"`-like declaration. */ + export interface PatternAmbientModule { + pattern: Pattern; + symbol: Symbol; + } + /* @internal */ export const enum NodeCheckFlags { TypeChecked = 0x00000001, // Node has been type checked diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt new file mode 100644 index 0000000000000..c197fe54f8dd7 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt @@ -0,0 +1,25 @@ +tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + +==== tests/cases/conformance/ambient/user.ts (0 errors) ==== + /// + import {foo} from "foobarbaz"; + foo(0); + + import {foos} from "foosball"; + +==== tests/cases/conformance/ambient/declarations.d.ts (1 errors) ==== + declare module "foo*baz" { + export function foo(n: number): void; + } + + // Should be an error + declare module "too*many*asterisks" { } + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + // Longest prefix wins + declare module "foos*" { + export const foos: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js new file mode 100644 index 0000000000000..056416f1086d1 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts] //// + +//// [declarations.d.ts] +declare module "foo*baz" { + export function foo(n: number): void; +} + +// Should be an error +declare module "too*many*asterisks" { } + +// Longest prefix wins +declare module "foos*" { + export const foos: number; +} + +//// [user.ts] +/// +import {foo} from "foobarbaz"; +foo(0); + +import {foos} from "foosball"; + + +//// [user.js] +"use strict"; +/// +var foobarbaz_1 = require("foobarbaz"); +foobarbaz_1.foo(0); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts new file mode 100644 index 0000000000000..0d3b2e1c60ead --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -0,0 +1,19 @@ +// @Filename: declarations.d.ts +declare module "foo*baz" { + export function foo(n: number): void; +} + +// Should be an error +declare module "too*many*asterisks" { } + +// Longest prefix wins +declare module "foos*" { + export const foos: number; +} + +// @Filename: user.ts +/// +import {foo} from "foobarbaz"; +foo(0); + +import {foos} from "foosball"; From 094f97e366ee0df43461bb813be8fe4a1df7dfc0 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 3 Jun 2016 06:22:34 -0700 Subject: [PATCH 2/5] Respond to PR comments --- src/compiler/binder.ts | 18 +++++++----------- src/compiler/checker.ts | 18 +++++------------- src/compiler/program.ts | 5 ++++- src/compiler/types.ts | 4 +++- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d3363285afcdd..753e02c7a09d4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1181,9 +1181,9 @@ namespace ts { lastContainer = next; } - function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { // Just call this directly so that the return type of this function stays "void". - declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); + return declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); } function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { @@ -1298,14 +1298,10 @@ namespace ts { } } + const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + if (pattern) { - // TODO: don't really need such a symbol in container.locals... - const symbol = declareSymbol(container.locals, undefined, node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); - file.patternAmbientModules = file.patternAmbientModules || []; - file.patternAmbientModules.push({ pattern, symbol }); - } - else { - declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + (file.patternAmbientModules || (file.patternAmbientModules = [])).push({ pattern, symbol }); } } } @@ -2084,10 +2080,10 @@ namespace ts { checkStrictModeFunctionName(node); if (inStrictMode) { checkStrictModeFunctionDeclaration(node); - return bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e66cf1661f52..149f21734a4ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1292,9 +1292,11 @@ namespace ts { return undefined; } - const patternModuleSymbol = getPatternAmbientModule(moduleName); - if (patternModuleSymbol) { - return getMergedSymbol(patternModuleSymbol); + if (patternAmbientModules) { + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName); + if (pattern) { + return getMergedSymbol(pattern.symbol); + } } if (moduleNotFoundError) { @@ -1304,16 +1306,6 @@ namespace ts { return undefined; } - /** Get an ambient module with a wildcard ("*") in it. */ - function getPatternAmbientModule(name: string): Symbol | undefined { - if (patternAmbientModules) { - const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, name); - if (pattern) { - return pattern.symbol; - } - } - } - // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1677f61c15df8..3267737a3e5a8 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -95,6 +95,7 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } + /* @internal */ export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { @@ -502,7 +503,7 @@ namespace ts { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - matchedPattern = matchPatternOrExact(Object.keys(state.compilerOptions.paths), moduleName); + matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { @@ -570,6 +571,7 @@ namespace ts { } /** Return the object corresponding to the best pattern to match `candidate`. */ + /* @internal */ export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { let matchedValue: T | undefined = undefined; // use length of prefix as betterness criteria @@ -592,6 +594,7 @@ namespace ts { endsWith(candidate, suffix); } + /* @internal */ export function tryParsePattern(pattern: string): Pattern | undefined { // This should be verified outside of here and a proper error thrown. Debug.assert(hasZeroOrOneAsteriskCharacter(pattern)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6f19f121cf01..bcc4bbcf6965d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2137,12 +2137,14 @@ namespace ts { } /** Represents a "prefix*suffix" pattern. */ + /* @internal */ export interface Pattern { prefix: string; suffix: string; } - + /** Used to track a `declare module "foo*"`-like declaration. */ + /* @internal */ export interface PatternAmbientModule { pattern: Pattern; symbol: Symbol; From f215ac94c2741c79f512896125c1f43b018052a2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 12:00:53 -0700 Subject: [PATCH 3/5] Add another test --- .../reference/ambientDeclarationsPatterns.errors.txt | 10 +++++++--- .../baselines/reference/ambientDeclarationsPatterns.js | 10 +++++++--- .../conformance/ambient/ambientDeclarationsPatterns.ts | 8 ++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt index c197fe54f8dd7..8fc7e4a3d18ae 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt +++ b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt @@ -1,10 +1,10 @@ -tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character +tests/cases/conformance/ambient/declarations.d.ts(10,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character ==== tests/cases/conformance/ambient/user.ts (0 errors) ==== /// - import {foo} from "foobarbaz"; - foo(0); + import {foo, baz} from "foobarbaz"; + foo(baz); import {foos} from "foosball"; @@ -12,6 +12,10 @@ tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern ' declare module "foo*baz" { export function foo(n: number): void; } + // Augmentations still work + declare module "foo*baz" { + export const baz: number; + } // Should be an error declare module "too*many*asterisks" { } diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js index 056416f1086d1..ee9833a3a37a7 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.js +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -4,6 +4,10 @@ declare module "foo*baz" { export function foo(n: number): void; } +// Augmentations still work +declare module "foo*baz" { + export const baz: number; +} // Should be an error declare module "too*many*asterisks" { } @@ -15,8 +19,8 @@ declare module "foos*" { //// [user.ts] /// -import {foo} from "foobarbaz"; -foo(0); +import {foo, baz} from "foobarbaz"; +foo(baz); import {foos} from "foosball"; @@ -25,4 +29,4 @@ import {foos} from "foosball"; "use strict"; /// var foobarbaz_1 = require("foobarbaz"); -foobarbaz_1.foo(0); +foobarbaz_1.foo(foobarbaz_1.baz); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts index 0d3b2e1c60ead..43e373ba9de56 100644 --- a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -2,6 +2,10 @@ declare module "foo*baz" { export function foo(n: number): void; } +// Augmentations still work +declare module "foo*baz" { + export const baz: number; +} // Should be an error declare module "too*many*asterisks" { } @@ -13,7 +17,7 @@ declare module "foos*" { // @Filename: user.ts /// -import {foo} from "foobarbaz"; -foo(0); +import {foo, baz} from "foobarbaz"; +foo(baz); import {foos} from "foosball"; From 03371c0e6f20d49fda583ff96a52e9d0c4967460 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 11:12:12 -0700 Subject: [PATCH 4/5] Improve perf --- src/compiler/checker.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 149f21734a4ce..8e5d5e61b752b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11291,7 +11291,7 @@ namespace ts { const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(declaration.parent.symbol); const declaringClass = getDeclaredTypeOfSymbol(declaration.parent.symbol); - // A private or protected constructor can only be instantiated within it's own class + // A private or protected constructor can only be instantiated within it's own class if (!isNodeWithinClass(node, declaringClassDeclaration)) { if (flags & NodeFlags.Private) { error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass)); @@ -16154,12 +16154,12 @@ namespace ts { const symbol = getSymbolOfNode(node); const target = resolveAlias(symbol); if (target !== unknownSymbol) { - // For external modules symbol represent local symbol for an alias. + // For external modules symbol represent local symbol for an alias. // This local symbol will merge any other local declarations (excluding other aliases) // and symbol.flags will contains combined representation for all merged declaration. // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, - // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* - // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). + // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* + // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). const excludedMeanings = (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | @@ -16358,7 +16358,7 @@ namespace ts { continue; } const { declarations, flags } = exports[id]; - // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. + // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. // (TS Exceptions: namespaces, function overloads, enums, and interfaces) if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) { continue; @@ -17063,10 +17063,10 @@ namespace ts { } // Gets the type of object literal or array literal of destructuring assignment. - // { a } from + // { a } from // for ( { a } of elems) { // } - // [ a ] from + // [ a ] from // [a] = [ some array ...] function getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type { Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression); @@ -17099,10 +17099,10 @@ namespace ts { } // Gets the property symbol corresponding to the property in destructuring assignment - // 'property1' from + // 'property1' from // for ( { property1: a } of elems) { // } - // 'property1' at location 'a' from: + // 'property1' at location 'a' from: // [a] = [ property1, property2 ] function getPropertySymbolOfDestructuringAssignment(location: Identifier) { // Get the type of the object or array literal and then look for property of given name in the type @@ -17642,7 +17642,7 @@ namespace ts { mergeSymbolTable(globals, file.locals); } if (file.patternAmbientModules && file.patternAmbientModules.length) { - (patternAmbientModules || (patternAmbientModules = [])).push(...file.patternAmbientModules); + patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules); } if (file.moduleAugmentations.length) { From 559b49baa9380bfd77681aa466444e1d9c37d5cf Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 11:33:53 -0700 Subject: [PATCH 5/5] Improve tests --- .../ambientDeclarationsPatterns.errors.txt | 29 ---------- .../reference/ambientDeclarationsPatterns.js | 24 ++++++--- .../ambientDeclarationsPatterns.symbols | 51 ++++++++++++++++++ .../ambientDeclarationsPatterns.types | 54 +++++++++++++++++++ ...ationsPatterns_tooManyAsterisks.errors.txt | 8 +++ ...ntDeclarationsPatterns_tooManyAsterisks.js | 5 ++ .../ambient/ambientDeclarationsPatterns.ts | 19 ++++--- ...ntDeclarationsPatterns_tooManyAsterisks.ts | 1 + 8 files changed, 150 insertions(+), 41 deletions(-) delete mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.symbols create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.types create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js create mode 100644 tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt deleted file mode 100644 index 8fc7e4a3d18ae..0000000000000 --- a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt +++ /dev/null @@ -1,29 +0,0 @@ -tests/cases/conformance/ambient/declarations.d.ts(10,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character - - -==== tests/cases/conformance/ambient/user.ts (0 errors) ==== - /// - import {foo, baz} from "foobarbaz"; - foo(baz); - - import {foos} from "foosball"; - -==== tests/cases/conformance/ambient/declarations.d.ts (1 errors) ==== - declare module "foo*baz" { - export function foo(n: number): void; - } - // Augmentations still work - declare module "foo*baz" { - export const baz: number; - } - - // Should be an error - declare module "too*many*asterisks" { } - ~~~~~~~~~~~~~~~~~~~~ -!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character - - // Longest prefix wins - declare module "foos*" { - export const foos: number; - } - \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js index ee9833a3a37a7..143f26550e997 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.js +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -2,19 +2,21 @@ //// [declarations.d.ts] declare module "foo*baz" { - export function foo(n: number): void; + export function foo(s: string): void; } // Augmentations still work declare module "foo*baz" { - export const baz: number; + export const baz: string; } -// Should be an error -declare module "too*many*asterisks" { } - // Longest prefix wins declare module "foos*" { - export const foos: number; + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; } //// [user.ts] @@ -23,6 +25,11 @@ import {foo, baz} from "foobarbaz"; foo(baz); import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); //// [user.js] @@ -30,3 +37,8 @@ import {foos} from "foosball"; /// var foobarbaz_1 = require("foobarbaz"); foobarbaz_1.foo(foobarbaz_1.baz); +var foosball_1 = require("foosball"); +foobarbaz_1.foo(foosball_1.foos); +// Works with relative file name +var file_text_1 = require("./file!text"); +foobarbaz_1.foo(file_text_1["default"]); diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.symbols b/tests/baselines/reference/ambientDeclarationsPatterns.symbols new file mode 100644 index 0000000000000..4c0acc93f8f93 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +foo(baz); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +import {foos} from "foosball"; +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +foo(foos); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +// Works with relative file name +import fileText from "./file!text"; +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +foo(fileText); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 26)) +>s : Symbol(s, Decl(declarations.d.ts, 1, 24)) +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : Symbol(baz, Decl(declarations.d.ts, 5, 16)) +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : Symbol(foos, Decl(declarations.d.ts, 10, 16)) +} + +declare module "*!text" { + const x: string; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) + + export default x; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.types b/tests/baselines/reference/ambientDeclarationsPatterns.types new file mode 100644 index 0000000000000..adf8ae1ab3b20 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.types @@ -0,0 +1,54 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : (s: string) => void +>baz : string + +foo(baz); +>foo(baz) : void +>foo : (s: string) => void +>baz : string + +import {foos} from "foosball"; +>foos : string + +foo(foos); +>foo(foos) : void +>foo : (s: string) => void +>foos : string + +// Works with relative file name +import fileText from "./file!text"; +>fileText : string + +foo(fileText); +>foo(fileText) : void +>foo : (s: string) => void +>fileText : string + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : (s: string) => void +>s : string +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : string +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : string +} + +declare module "*!text" { + const x: string; +>x : string + + export default x; +>x : string +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt new file mode 100644 index 0000000000000..7a3ff02aa5e67 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt @@ -0,0 +1,8 @@ +tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts(1,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + +==== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts (1 errors) ==== + declare module "too*many*asterisks" { } + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js new file mode 100644 index 0000000000000..a664eb84dc5aa --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js @@ -0,0 +1,5 @@ +//// [ambientDeclarationsPatterns_tooManyAsterisks.ts] +declare module "too*many*asterisks" { } + + +//// [ambientDeclarationsPatterns_tooManyAsterisks.js] diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts index 43e373ba9de56..d48f50bfa5059 100644 --- a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -1,18 +1,20 @@ // @Filename: declarations.d.ts declare module "foo*baz" { - export function foo(n: number): void; + export function foo(s: string): void; } // Augmentations still work declare module "foo*baz" { - export const baz: number; + export const baz: string; } -// Should be an error -declare module "too*many*asterisks" { } - // Longest prefix wins declare module "foos*" { - export const foos: number; + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; } // @Filename: user.ts @@ -21,3 +23,8 @@ import {foo, baz} from "foobarbaz"; foo(baz); import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts new file mode 100644 index 0000000000000..76f9081906ca8 --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts @@ -0,0 +1 @@ +declare module "too*many*asterisks" { }