Skip to content

Commit

Permalink
Add 'declaredCount' for easy duplicate index check.
Browse files Browse the repository at this point in the history
Fix fourslash test.
Add potential cases for union as an index signature.
  • Loading branch information
jbondc committed Apr 14, 2015
1 parent 0dd4399 commit 93550a8
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 218 deletions.
105 changes: 44 additions & 61 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ module ts {
let nextMergeId = 1;

interface IndexTypeMap {
[x: number]: IndexType
[x: IndexKind]: IndexType
}
// TODO: LKG and replace [x: IndexKind]

// @internal
export function getNodeId(node: Node): number {
Expand Down Expand Up @@ -1637,7 +1636,8 @@ module ts {
}

function getIndexerParameterName(type: ObjectType, indexKind: IndexKind, fallbackName: string): string {
let declaration = <SignatureDeclaration>getIndexDeclarationOfSymbol(type.symbol, indexKind);
let indexMap = getDeclaredIndexTypesOfSymbol(type.symbol);
let declaration = indexMap[indexKind] ? indexMap[indexKind].declaredNode : null;
if (!declaration) {
// declaration might not be found if indexer was added from the contextual type.
// in this case use fallback name
Expand Down Expand Up @@ -1696,21 +1696,21 @@ module ts {
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
function writeIndexType(indexType: IndexType, kind: IndexKind) {
function writeIndexType(indexType: IndexType) {
if (!indexType) {
return;
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writer.writeParameter(getIndexerParameterName(resolved, kind, /*fallbackName*/"x"));
writer.writeParameter(getIndexerParameterName(resolved, indexType.kind, /*fallbackName*/"x"));
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);

if (indexType.typeOfIndex && (indexType.typeOfIndex.flags & TypeFlags.Enum)) {
if (indexType.typeOfIndex && (indexType.typeOfIndex.flags & TypeFlags.Subset)) {
writeType(indexType.typeOfIndex, TypeFormatFlags.None);
writer.writeStringLiteral(kind === IndexKind.String ? "<string>" : "<number>");
writer.writeStringLiteral(indexType.kind === IndexKind.String ? " < string" : " < number");
}
else {
writeKeyword(writer, kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
writeKeyword(writer, indexType.kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
}

writePunctuation(writer, SyntaxKind.CloseBracketToken);
Expand All @@ -1720,8 +1720,8 @@ module ts {
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
writeIndexType(resolved.stringIndex, IndexKind.String)
writeIndexType(resolved.numberIndex, IndexKind.Number)
writeIndexType(resolved.stringIndex)
writeIndexType(resolved.numberIndex)

for (let p of resolved.properties) {
let t = getTypeOfSymbol(p);
Expand Down Expand Up @@ -3352,25 +3352,6 @@ module ts {
return symbol.members["__index"];
}

function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
let flagCheck = kind === IndexKind.Number ? TypeFlags.NumberLike : TypeFlags.String;
let indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
let len = indexSymbol.declarations.length;
for (let decl of indexSymbol.declarations) {
let node = <SignatureDeclaration>decl;
if (node.parameters.length === 1) {
let type = getTypeFromIndexSignatureParameter(node.parameters[0]);
if(type && (type.flags & flagCheck)) {
return node;
}
}
}
}

return undefined;
}

function getDeclaredIndexTypesOfSymbol(symbol: Symbol): IndexTypeMap {
let indexMap: IndexTypeMap = []
let indexSymbol = getIndexSymbol(symbol);
Expand All @@ -3382,12 +3363,17 @@ module ts {
continue;
}
let kind = (type.flags & TypeFlags.NumberLike) ? IndexKind.Number : (type.flags & TypeFlags.String) ? IndexKind.String: null;
if(kind !== null) {
indexMap[kind] = {
kind: kind,
typeOfIndex: type,
typeOfValue: decl.type ? getTypeFromTypeNodeOrHeritageClauseElement(decl.type) : anyType,
declaredNode: decl
if (kind !== null) {
if (indexMap[kind]) {
indexMap[kind].declaredCount++;
} else {
indexMap[kind] = {
kind: kind,
typeOfIndex: type,
typeOfValue: decl.type ? getTypeFromTypeNodeOrHeritageClauseElement(decl.type) : anyType,
declaredNode: decl,
declaredCount: 1
}
}
}
}
Expand Down Expand Up @@ -8326,34 +8312,31 @@ module ts {
// TypeScript 1.0 spec (April 2014)
// 3.7.4: An object type can contain at most one string index signature and one numeric index signature.
// 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration
let indexSymbol = getIndexSymbol(getSymbolOfNode(node));
if (indexSymbol) {
let seenNumericIndexer = false;
let seenStringIndexer = false;
let symbol = getSymbolOfNode(node);
let indexMap = getDeclaredIndexTypesOfSymbol(symbol);
let stringIndex = indexMap[IndexKind.String];
let numberIndex = indexMap[IndexKind.Number];

if (stringIndex && stringIndex.declaredCount > 1) {
errorDuplicateIndex(getIndexSymbol(symbol), stringIndex);
}

if (numberIndex && numberIndex.declaredCount > 1) {
errorDuplicateIndex(getIndexSymbol(symbol), numberIndex);
}

function errorDuplicateIndex(indexSymbol: Symbol, indexType: IndexType) {
for (let decl of indexSymbol.declarations) {
let declaration = <SignatureDeclaration>decl;
if (declaration.parameters.length === 1) {
let type = getTypeFromIndexSignatureParameter(declaration.parameters[0])
if (!type) {
continue;
}

if (type.flags & TypeFlags.String) {
if (!seenStringIndexer) {
seenStringIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_string_index_signature);
}
}
else if (type.flags & TypeFlags.NumberLike) {
if (!seenNumericIndexer) {
seenNumericIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_number_index_signature);
}
}
if (declaration.parameters.length !== 1 || declaration === indexType.declaredNode) {
continue;
}
let type = getTypeFromIndexSignatureParameter(declaration.parameters[0])
if (type.flags & TypeFlags.String && indexType.kind === IndexKind.String) {
error(declaration, Diagnostics.Duplicate_string_index_signature);
}
else if (type.flags & TypeFlags.NumberLike && indexType.kind === IndexKind.Number) {
error(declaration, Diagnostics.Duplicate_number_index_signature);
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1527,8 +1527,11 @@ module ts {
kind: IndexKind // Kind of index
typeOfValue: Type // any
typeOfIndex?: Type // string|number|enum
declaredNode?: Declaration, // Declaration of [x: typeOfIndex]: typeOfValue
inherited?: Symbol // Symbol of baseType where inherited

// Useful for error reporting
declaredNode?: SignatureDeclaration, // Declaration of [x: typeOfIndex]: typeOfValue
declaredCount?: number // Number of declarations
inherited?: Symbol // Symbol of baseType where inherited
}

/* @internal */ // Resolved object or union type
Expand Down
50 changes: 25 additions & 25 deletions tests/baselines/reference/APISample_compile.types
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
>fileNames : string[], Symbol(fileNames, Decl(APISample_compile.ts, 13, 24))
>options : ts.CompilerOptions, Symbol(options, Decl(APISample_compile.ts, 13, 44))
>ts : any, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>CompilerOptions : ts.CompilerOptions, Symbol(ts.CompilerOptions, Decl(typescript.d.ts, 1088, 5))
>CompilerOptions : ts.CompilerOptions, Symbol(ts.CompilerOptions, Decl(typescript.d.ts, 1089, 5))

var program = ts.createProgram(fileNames, options);
>program : ts.Program, Symbol(program, Decl(APISample_compile.ts, 14, 7))
>ts.createProgram(fileNames, options) : ts.Program
>ts.createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1215, 113))
>ts.createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1216, 113))
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1215, 113))
>createProgram : (rootNames: string[], options: ts.CompilerOptions, host?: ts.CompilerHost) => ts.Program, Symbol(ts.createProgram, Decl(typescript.d.ts, 1216, 113))
>fileNames : string[], Symbol(fileNames, Decl(APISample_compile.ts, 13, 24))
>options : ts.CompilerOptions, Symbol(options, Decl(APISample_compile.ts, 13, 44))

Expand All @@ -46,9 +46,9 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
>ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics) : ts.Diagnostic[]
>ts.getPreEmitDiagnostics(program).concat : { <U extends ts.Diagnostic[]>(...items: U[]): ts.Diagnostic[]; (...items: ts.Diagnostic[]): ts.Diagnostic[]; }, Symbol(Array.concat, Decl(lib.d.ts, 1025, 13), Decl(lib.d.ts, 1030, 46))
>ts.getPreEmitDiagnostics(program) : ts.Diagnostic[]
>ts.getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1213, 98))
>ts.getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1214, 98))
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1213, 98))
>getPreEmitDiagnostics : (program: ts.Program) => ts.Diagnostic[], Symbol(ts.getPreEmitDiagnostics, Decl(typescript.d.ts, 1214, 98))
>program : ts.Program, Symbol(program, Decl(APISample_compile.ts, 14, 7))
>concat : { <U extends ts.Diagnostic[]>(...items: U[]): ts.Diagnostic[]; (...items: ts.Diagnostic[]): ts.Diagnostic[]; }, Symbol(Array.concat, Decl(lib.d.ts, 1025, 13), Decl(lib.d.ts, 1030, 46))
>emitResult.diagnostics : ts.Diagnostic[], Symbol(ts.EmitResult.diagnostics, Decl(typescript.d.ts, 820, 29))
Expand All @@ -67,24 +67,24 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
>line : number, Symbol(line, Decl(APISample_compile.ts, 20, 13))
>character : number, Symbol(character, Decl(APISample_compile.ts, 20, 19))
>diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start) : ts.LineAndCharacter
>diagnostic.file.getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1277, 46))
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
>diagnostic.file.getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1278, 46))
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
>getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1277, 46))
>diagnostic.start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1077, 25))
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
>getLineAndCharacterOfPosition : (pos: number) => ts.LineAndCharacter, Symbol(ts.SourceFile.getLineAndCharacterOfPosition, Decl(typescript.d.ts, 1278, 46))
>diagnostic.start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1078, 25))
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
>start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1077, 25))
>start : number, Symbol(ts.Diagnostic.start, Decl(typescript.d.ts, 1078, 25))

var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
>message : string, Symbol(message, Decl(APISample_compile.ts, 21, 11))
>ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') : string
>ts.flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1214, 67))
>ts.flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1215, 67))
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1214, 67))
>diagnostic.messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1079, 23))
>flattenDiagnosticMessageText : (messageText: string | ts.DiagnosticMessageChain, newLine: string) => string, Symbol(ts.flattenDiagnosticMessageText, Decl(typescript.d.ts, 1215, 67))
>diagnostic.messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1080, 23))
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
>messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1079, 23))
>messageText : string | ts.DiagnosticMessageChain, Symbol(ts.Diagnostic.messageText, Decl(typescript.d.ts, 1080, 23))
>'\n' : string

console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
Expand All @@ -94,9 +94,9 @@ export function compile(fileNames: string[], options: ts.CompilerOptions): void
>log : any
>`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}` : string
>diagnostic.file.fileName : string, Symbol(ts.SourceFile.fileName, Decl(typescript.d.ts, 743, 29))
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
>diagnostic.file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
>diagnostic : ts.Diagnostic, Symbol(diagnostic, Decl(APISample_compile.ts, 19, 27))
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1076, 26))
>file : ts.SourceFile, Symbol(ts.Diagnostic.file, Decl(typescript.d.ts, 1077, 26))
>fileName : string, Symbol(ts.SourceFile.fileName, Decl(typescript.d.ts, 743, 29))
>line + 1 : number
>line : number, Symbol(line, Decl(APISample_compile.ts, 20, 13))
Expand Down Expand Up @@ -153,16 +153,16 @@ compile(process.argv.slice(2), {

target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
>target : ts.ScriptTarget, Symbol(target, Decl(APISample_compile.ts, 31, 45))
>ts.ScriptTarget.ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1131, 16))
>ts.ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1129, 5))
>ts.ScriptTarget.ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1132, 16))
>ts.ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1130, 5))
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1129, 5))
>ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1131, 16))
>ScriptTarget : typeof ts.ScriptTarget, Symbol(ts.ScriptTarget, Decl(typescript.d.ts, 1130, 5))
>ES5 : ts.ScriptTarget, Symbol(ts.ScriptTarget.ES5, Decl(typescript.d.ts, 1132, 16))
>module : ts.ModuleKind, Symbol(module, Decl(APISample_compile.ts, 32, 32))
>ts.ModuleKind.CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1122, 17))
>ts.ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1120, 5))
>ts.ModuleKind.CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1123, 17))
>ts.ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1121, 5))
>ts : typeof ts, Symbol(ts, Decl(APISample_compile.ts, 9, 20))
>ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1120, 5))
>CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1122, 17))
>ModuleKind : typeof ts.ModuleKind, Symbol(ts.ModuleKind, Decl(typescript.d.ts, 1121, 5))
>CommonJS : ts.ModuleKind, Symbol(ts.ModuleKind.CommonJS, Decl(typescript.d.ts, 1123, 17))

});
Loading

0 comments on commit 93550a8

Please sign in to comment.