Skip to content

Commit f8134d0

Browse files
committed
Merge branch 'master' into js-object-literal-assignments-as-declarations
2 parents 35730f2 + 9586288 commit f8134d0

Some content is hidden

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

42 files changed

+17309
-16565
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ namespace ts {
315315
}
316316

317317
function getDisplayName(node: Declaration): string {
318-
return (node as NamedDeclaration).name ? declarationNameToString((node as NamedDeclaration).name) : unescapeLeadingUnderscores(getDeclarationName(node));
318+
return isNamedDeclaration(node) ? declarationNameToString(node.name) : unescapeLeadingUnderscores(getDeclarationName(node));
319319
}
320320

321321
/**
@@ -383,8 +383,8 @@ namespace ts {
383383
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
384384
}
385385
else {
386-
if ((node as NamedDeclaration).name) {
387-
(node as NamedDeclaration).name.parent = node;
386+
if (isNamedDeclaration(node)) {
387+
node.name.parent = node;
388388
}
389389

390390
// Report errors every position with duplicate declaration
@@ -1996,7 +1996,7 @@ namespace ts {
19961996

19971997
/// Should be called only on prologue directives (isPrologueDirective(node) should be true)
19981998
function isUseStrictPrologueDirective(node: ExpressionStatement): boolean {
1999-
const nodeText = getTextOfNodeFromSourceText(file.text, node.expression);
1999+
const nodeText = getSourceTextOfNodeFromSourceFile(file, node.expression);
20002000

20012001
// Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the
20022002
// string to contain unicode escapes (as per ES5).

src/compiler/checker.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8012,10 +8012,10 @@ namespace ts {
80128012
}
80138013

80148014
function getLiteralTypeFromPropertyName(prop: Symbol) {
8015-
const links = getSymbolLinks(prop);
8015+
const links = getSymbolLinks(getLateBoundSymbol(prop));
80168016
if (!links.nameType) {
80178017
if (links.target) {
8018-
Debug.assert(links.target.escapedName === prop.escapedName, "Target symbol and symbol do not have the same name");
8018+
Debug.assert(links.target.escapedName === prop.escapedName || links.target.escapedName === InternalSymbolName.Computed, "Target symbol and symbol do not have the same name");
80198019
links.nameType = getLiteralTypeFromPropertyName(links.target);
80208020
}
80218021
else {
@@ -10562,6 +10562,11 @@ namespace ts {
1056210562
if (isIgnoredJsxProperty(source, prop, /*targetMemberType*/ undefined)) {
1056310563
continue;
1056410564
}
10565+
// Skip over symbol-named members
10566+
const nameType = getLiteralTypeFromPropertyName(prop);
10567+
if (nameType !== undefined && !(isRelatedTo(nameType, stringType) || isRelatedTo(nameType, numberType))) {
10568+
continue;
10569+
}
1056510570
if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) {
1056610571
const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors);
1056710572
if (!related) {
@@ -21737,18 +21742,58 @@ namespace ts {
2173721742

2173821743
function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void {
2173921744
if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
21745+
// Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
21746+
const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
2174021747
node.locals.forEach(local => {
21741-
if (!local.isReferenced && !local.exportSymbol) {
21742-
for (const declaration of local.declarations) {
21743-
if (!isAmbientModule(declaration)) {
21744-
errorUnusedLocal(declaration, symbolName(local));
21748+
if (local.isReferenced || local.exportSymbol) return;
21749+
for (const declaration of local.declarations) {
21750+
if (isAmbientModule(declaration)) continue;
21751+
if (isImportedDeclaration(declaration)) {
21752+
const importClause = importClauseFromImported(declaration);
21753+
const key = String(getNodeId(importClause));
21754+
const group = unusedImports.get(key);
21755+
if (group) {
21756+
group[1].push(declaration);
21757+
}
21758+
else {
21759+
unusedImports.set(key, [importClause, [declaration]]);
2174521760
}
2174621761
}
21762+
else {
21763+
errorUnusedLocal(declaration, symbolName(local));
21764+
}
21765+
}
21766+
});
21767+
21768+
unusedImports.forEach(([importClause, unuseds]) => {
21769+
const importDecl = importClause.parent;
21770+
if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
21771+
for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name));
21772+
}
21773+
else if (unuseds.length === 1) {
21774+
error(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name));
21775+
}
21776+
else {
21777+
error(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl));
2174721778
}
2174821779
});
2174921780
}
2175021781
}
2175121782

21783+
type ImportedDeclaration = ImportClause | ImportSpecifier | NamespaceImport;
21784+
function isImportedDeclaration(node: Node): node is ImportedDeclaration {
21785+
return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.NamespaceImport;
21786+
}
21787+
function importClauseFromImported(decl: ImportedDeclaration): ImportClause {
21788+
return decl.kind === SyntaxKind.ImportClause ? decl : decl.kind === SyntaxKind.NamespaceImport ? decl.parent : decl.parent.parent;
21789+
}
21790+
21791+
function forEachImportedDeclaration<T>(importClause: ImportClause, cb: (im: ImportedDeclaration) => T | undefined): T | undefined {
21792+
const { name: defaultName, namedBindings } = importClause;
21793+
return (defaultName && cb(importClause)) ||
21794+
namedBindings && (namedBindings.kind === SyntaxKind.NamespaceImport ? cb(namedBindings) : forEach(namedBindings.elements, cb));
21795+
}
21796+
2175221797
function checkBlock(node: Block) {
2175321798
// Grammar checking for SyntaxKind.Block
2175421799
if (node.kind === SyntaxKind.Block) {
@@ -22867,8 +22912,7 @@ namespace ts {
2286722912
return "quit";
2286822913
}
2286922914
if (current.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>current).label.escapedText === node.label.escapedText) {
22870-
const sourceFile = getSourceFileOfNode(node);
22871-
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label));
22915+
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNode(node.label));
2287222916
return true;
2287322917
}
2287422918
});

src/compiler/core.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,7 +2539,6 @@ namespace ts {
25392539
path = normalizePath(path);
25402540
currentDirectory = normalizePath(currentDirectory);
25412541

2542-
const comparer = useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
25432542
const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory);
25442543

25452544
const regexFlag = useCaseSensitiveFileNames ? "" : "i";
@@ -2560,7 +2559,7 @@ namespace ts {
25602559
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
25612560
const { files, directories } = getFileSystemEntries(path);
25622561

2563-
for (const current of sort(files, comparer)) {
2562+
for (const current of sort(files, compareStringsCaseSensitive)) {
25642563
const name = combinePaths(path, current);
25652564
const absoluteName = combinePaths(absolutePath, current);
25662565
if (extensions && !fileExtensionIsOneOf(name, extensions)) continue;
@@ -2583,7 +2582,7 @@ namespace ts {
25832582
}
25842583
}
25852584

2586-
for (const current of sort(directories, comparer)) {
2585+
for (const current of sort(directories, compareStringsCaseSensitive)) {
25872586
const name = combinePaths(path, current);
25882587
const absoluteName = combinePaths(absolutePath, current);
25892588
if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3480,6 +3480,10 @@
34803480
"category": "Message",
34813481
"code": 6191
34823482
},
3483+
"All imports in import declaration are unused.": {
3484+
"category": "Error",
3485+
"code": 6192
3486+
},
34833487
"Variable '{0}' implicitly has an '{1}' type.": {
34843488
"category": "Error",
34853489
"code": 7005
@@ -3851,6 +3855,10 @@
38513855
"category": "Message",
38523856
"code": 90004
38533857
},
3858+
"Remove import from '{0}'": {
3859+
"category": "Message",
3860+
"code": 90005
3861+
},
38543862
"Implement interface '{0}'": {
38553863
"category": "Message",
38563864
"code": 90006

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,6 +2793,7 @@ namespace ts {
27932793

27942794
/** Note that the resulting nodes cannot be checked. */
27952795
typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode;
2796+
/* @internal */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker): TypeNode; // tslint:disable-line unified-signatures
27962797
/** Note that the resulting nodes cannot be checked. */
27972798
signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration & {typeArguments?: NodeArray<TypeNode>} | undefined;
27982799
/** Note that the resulting nodes cannot be checked. */

src/compiler/utilities.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,20 +314,15 @@ namespace ts {
314314
}
315315

316316
export function getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node, includeTrivia = false): string {
317-
if (nodeIsMissing(node)) {
318-
return "";
319-
}
320-
321-
const text = sourceFile.text;
322-
return text.substring(includeTrivia ? node.pos : skipTrivia(text, node.pos), node.end);
317+
return getTextOfNodeFromSourceText(sourceFile.text, node, includeTrivia);
323318
}
324319

325-
export function getTextOfNodeFromSourceText(sourceText: string, node: Node): string {
320+
export function getTextOfNodeFromSourceText(sourceText: string, node: Node, includeTrivia = false): string {
326321
if (nodeIsMissing(node)) {
327322
return "";
328323
}
329324

330-
return sourceText.substring(skipTrivia(sourceText, node.pos), node.end);
325+
return sourceText.substring(includeTrivia ? node.pos : skipTrivia(sourceText, node.pos), node.end);
331326
}
332327

333328
export function getTextOfNode(node: Node, includeTrivia = false): string {
@@ -1204,7 +1199,7 @@ namespace ts {
12041199
&& (<PropertyAccessExpression | ElementAccessExpression>node).expression.kind === SyntaxKind.ThisKeyword;
12051200
}
12061201

1207-
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression {
1202+
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression {
12081203
switch (node.kind) {
12091204
case SyntaxKind.TypeReference:
12101205
return (<TypeReferenceNode>node).typeName;
@@ -2591,7 +2586,6 @@ namespace ts {
25912586
const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
25922587
const backtickQuoteEscapedCharsRegExp = /[\\\`\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
25932588
const escapedCharsMap = createMapFromTemplate({
2594-
"\0": "\\0",
25952589
"\t": "\\t",
25962590
"\v": "\\v",
25972591
"\f": "\\f",
@@ -2606,7 +2600,6 @@ namespace ts {
26062600
"\u2029": "\\u2029", // paragraphSeparator
26072601
"\u0085": "\\u0085" // nextLine
26082602
});
2609-
const escapedNullRegExp = /\\0[0-9]/g;
26102603

26112604
/**
26122605
* Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
@@ -2618,14 +2611,19 @@ namespace ts {
26182611
quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp :
26192612
quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp :
26202613
doubleQuoteEscapedCharsRegExp;
2621-
return s.replace(escapedCharsRegExp, getReplacement).replace(escapedNullRegExp, nullReplacement);
2622-
}
2623-
2624-
function nullReplacement(c: string) {
2625-
return "\\x00" + c.charAt(c.length - 1);
2614+
return s.replace(escapedCharsRegExp, getReplacement);
26262615
}
26272616

2628-
function getReplacement(c: string) {
2617+
function getReplacement(c: string, offset: number, input: string) {
2618+
if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
2619+
const lookAhead = input.charCodeAt(offset + c.length);
2620+
if (lookAhead >= CharacterCodes._0 && lookAhead <= CharacterCodes._9) {
2621+
// If the null character is followed by digits, print as a hex escape to prevent the result from parsing as an octal (which is forbidden in strict mode)
2622+
return "\\x00";
2623+
}
2624+
// Otherwise, keep printing a literal \0 for the null character
2625+
return "\\0";
2626+
}
26292627
return escapedCharsMap.get(c) || get16BitUnicodeEscapeSequence(c.charCodeAt(0));
26302628
}
26312629

@@ -3848,6 +3846,10 @@ namespace ts {
38483846
export function isUMDExportSymbol(symbol: Symbol) {
38493847
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
38503848
}
3849+
3850+
export function showModuleSpecifier({ moduleSpecifier }: ImportDeclaration): string {
3851+
return isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : getTextOfNode(moduleSpecifier);
3852+
}
38513853
}
38523854

38533855
namespace ts {
@@ -4363,6 +4365,11 @@ namespace ts {
43634365
return declaration.name || nameForNamelessJSDocTypedef(declaration);
43644366
}
43654367

4368+
/** @internal */
4369+
export function isNamedDeclaration(node: Node): node is NamedDeclaration & { name: DeclarationName } {
4370+
return !!(node as NamedDeclaration).name; // A 'name' property should always be a DeclarationName.
4371+
}
4372+
43664373
export function getNameOfDeclaration(declaration: Declaration | Expression): DeclarationName | undefined {
43674374
if (!declaration) {
43684375
return undefined;

src/harness/typeWriter.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ class TypeWriterWalker {
7272
private writeTypeOrSymbol(node: ts.Node, isSymbolWalk: boolean): TypeWriterResult {
7373
const actualPos = ts.skipTrivia(this.currentSourceFile.text, node.pos);
7474
const lineAndCharacter = this.currentSourceFile.getLineAndCharacterOfPosition(actualPos);
75-
const sourceText = ts.getTextOfNodeFromSourceText(this.currentSourceFile.text, node);
76-
75+
const sourceText = ts.getSourceTextOfNodeFromSourceFile(this.currentSourceFile, node);
7776

7877
if (!isSymbolWalk) {
7978
// Workaround to ensure we output 'C' instead of 'typeof C' for base class expressions

src/harness/unittests/matchFiles.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ namespace ts {
9191
"c:/dev/g.min.js/.g/g.ts"
9292
]);
9393

94+
const caseInsensitiveOrderingDiffersWithCaseHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, [
95+
"c:/dev/xylophone.ts",
96+
"c:/dev/Yosemite.ts",
97+
"c:/dev/zebra.ts",
98+
]);
99+
100+
const caseSensitiveOrderingDiffersWithCaseHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, [
101+
"/dev/xylophone.ts",
102+
"/dev/Yosemite.ts",
103+
"/dev/zebra.ts",
104+
]);
105+
94106
function assertParsed(actual: ParsedCommandLine, expected: ParsedCommandLine): void {
95107
assert.deepEqual(actual.fileNames, expected.fileNames);
96108
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
@@ -1482,5 +1494,25 @@ namespace ts {
14821494
validateMatches(expected, json, caseSensitiveHost, caseSensitiveBasePath);
14831495
});
14841496
});
1497+
1498+
it("can include files in the same order on multiple platforms", () => {
1499+
function getExpected(basePath: string): ParsedCommandLine {
1500+
return {
1501+
options: {},
1502+
errors: [],
1503+
fileNames: [
1504+
`${basePath}Yosemite.ts`, // capital always comes before lowercase letters
1505+
`${basePath}xylophone.ts`,
1506+
`${basePath}zebra.ts`
1507+
],
1508+
wildcardDirectories: {
1509+
[basePath.slice(0, basePath.length - 1)]: WatchDirectoryFlags.Recursive
1510+
},
1511+
};
1512+
}
1513+
const json = {};
1514+
validateMatches(getExpected(caseSensitiveBasePath), json, caseSensitiveOrderingDiffersWithCaseHost, caseSensitiveBasePath);
1515+
validateMatches(getExpected(caseInsensitiveBasePath), json, caseInsensitiveOrderingDiffersWithCaseHost, caseInsensitiveBasePath);
1516+
});
14851517
});
14861518
}

0 commit comments

Comments
 (0)