Handle TODO's

This commit is contained in:
Arthur Ozga 2017-03-17 11:54:51 -07:00
parent 4fa32a29ce
commit ac7fc8fe75
6 changed files with 102 additions and 50 deletions

View file

@ -2203,13 +2203,12 @@ namespace ts {
return createTypeParameterDeclaration(name, constraint, defaultParameter);
}
// TODO: enclosing declaration appears to be unused in getTypeOfSymbolAtLocation
function createParameterDeclarationFromSymbol(parameterSymbol: Symbol, enclosingDeclaration: Node): ParameterDeclaration {
const parameterDeclaration = parameterSymbol.declarations[0] as ParameterDeclaration;
const parameterType = getTypeOfSymbol(parameterSymbol);
const parameterTypeNode = createTypeNode(parameterType, enclosingDeclaration);
// TODO: clone binding names correctly.
// TODO: copy initialzer in a way that checks whether all symbols used in expression are accessible here, and qualify them appropriately.
// TODO: how should we clone members/modifiers?
// TODO: check initializer accessibility correctly.
const parameterNode = createParameter(
parameterDeclaration.decorators && parameterDeclaration.decorators.map(getSynthesizedDeepClone)
, parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedDeepClone)
@ -2217,11 +2216,10 @@ namespace ts {
, getSynthesizedDeepClone(parameterDeclaration.name)
, parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken)
, parameterTypeNode
, /*initializer*/ undefined);
, parameterDeclaration.initializer && getSynthesizedDeepClone(parameterDeclaration.initializer));
return parameterNode;
}
// TODO: expose this, remove copy from helper, possibly don't expose createParameter/TypeParameter?
function createSignatureParts(signature: Signature, enclosingDeclaration: Node): SignatureParts {
return {
typeParameters: signature.typeParameters && signature.typeParameters.map(parameter => createTypeParameterDeclarationFromType(parameter,enclosingDeclaration)),
@ -2242,12 +2240,8 @@ namespace ts {
let checkAlias = true;
let symbolStack: Symbol[] = undefined;
let result = createTypeNodeWorker(type);
if (result) {
(<any>result).__type_source = type;
(<any>result).__type_source_str = typeToString(type);
}
return result;
const result = createTypeNodeWorker(type);
return encounteredError ? undefined: result;
function createTypeNodeWorker(type: Type): TypeNode {
if (!type) {
@ -2255,10 +2249,7 @@ namespace ts {
return undefined;
}
const typeString = typeToString(type, enclosingDeclaration); typeString; // TODO: remove.
if (type.flags & TypeFlags.Any) {
// TODO: add other case where type ends up being `any`.
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
if (type.flags & TypeFlags.String) {
@ -2318,19 +2309,16 @@ namespace ts {
}
if (objectFlags & ObjectFlags.ClassOrInterface) {
Debug.assert(!!(type.flags & TypeFlags.Object));
// TODO: Detect whether class is named and fail if not.
const name = createNameFromSymbol(type.symbol);
// TODO: handle type arguments.
return createTypeReferenceNode(name, /*typeParameters*/undefined);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.TypeParameter) {
// TODO: get qualified name when necessary instead of string.
const name = createNameFromSymbol(type.symbol);
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
// TODO: move back up later on?
if (checkAlias && type.aliasSymbol) {
const name = createNameFromSymbol(type.aliasSymbol);
const typeArgumentNodes = mapToTypeNodeArray(type.aliasTypeArguments);
@ -2352,18 +2340,14 @@ namespace ts {
return createAnonymousTypeNode(<ObjectType>type);
}
// TODO: implement when this is testable.
// else if (type.flags & TypeFlags.StringOrNumberLiteral) {
// writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
// TODO (aozgaa): implement string and number literals here once there is a testable case.
if (type.flags & TypeFlags.Index) {
// TODO: test.
const indexType = getIndexType(getApparentType((<IndexType>type).type));
const indexTypeNode = createTypeNodeWorker(indexType);
const indexedType = (<IndexType>type).type;
const indexTypeNode = createTypeNodeWorker(indexedType);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
// TODO: test.
const objectTypeNode = createTypeNodeWorker((<IndexedAccessType>type).objectType);
const indexTypeNode = createTypeNodeWorker((<IndexedAccessType>type).indexType);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
@ -2375,27 +2359,15 @@ namespace ts {
return types && asNodeArray(types.map(createTypeNodeWorker) as TypeNode[]);
}
function createNameFromSymbol(symbol: Symbol): Identifier;
function createNameFromSymbol(symbol: Symbol): EntityName;
function createNameFromSymbol(symbol: Symbol): EntityName {
symbol; enclosingDeclaration;
// TODO: actually implement this
return createIdentifier(symbolToString(symbol, enclosingDeclaration));
}
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
// TODO: does typeParameter have the same constraint or do we need to overwrite it somehow?
const typeParameter = getTypeParameterFromMappedType(<MappedType>type);
// const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
const typeParameterNode = createTypeParameterDeclarationFromType(typeParameter, enclosingDeclaration);
const templateTypeNode = createTypeNode(getTemplateTypeFromMappedType(<MappedType>type), enclosingDeclaration);
const readonlyToken = (<MappedType>type).declaration && (<MappedType>type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = (<MappedType>type).declaration && (<MappedType>type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
// TODO: test.
return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
}
@ -2501,7 +2473,6 @@ namespace ts {
return createTupleTypeNode(typeArguments.length > 0 ? mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))) : undefined);
}
else {
// TODO: handle type parameters in qualified names...
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
let qualifiedName: QualifiedName | undefined = undefined;
@ -2516,10 +2487,9 @@ namespace ts {
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
// TODO: figure out how to handle type arguments
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
const name = createNameFromSymbol(parent);
const qualifiedNamePart = name; // createTypeReferenceNode(name, mapToTypeNodeArray(typeArguments.slice(start, i - start)));
const qualifiedNamePart = name;
if (!qualifiedName) {
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/undefined);
}
@ -2534,7 +2504,6 @@ namespace ts {
let entityName: EntityName = undefined;
const nameIdentifier = createNameFromSymbol(type.symbol);
if (qualifiedName) {
// TODO: handle checking of type arguments for qualified names?
Debug.assert(!qualifiedName.right);
qualifiedName.right = nameIdentifier;
entityName = qualifiedName;
@ -2597,6 +2566,68 @@ namespace ts {
}
return typeElements.length ? typeElements : undefined;
}
function createNameFromSymbol(symbol: Symbol): Identifier;
function createNameFromSymbol(symbol: Symbol): EntityName;
function createNameFromSymbol(symbol: Symbol): EntityName {
let parentSymbol: Symbol;
symbol; enclosingDeclaration;
let meaning: SymbolFlags;
// Get qualified name if the symbol is not a type parameter
// and there is an enclosing declaration.
let chain: Symbol[];
const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
if (!isTypeParameter && enclosingDeclaration) {
chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true);
Debug.assert(chain && chain.length > 0);
}
else {
chain = [symbol];
}
const result = createEntityNameFromSymbolChain(chain, chain.length - 1);
return result;
function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName {
Debug.assert(chain && 0 <= index && index < chain.length);
const identifier = createIdentifier(getNameOfSymbol(chain[index]));
return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier;
}
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined {
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false);
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false);
if (parentChain) {
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]);
}
}
}
if (accessibleSymbolChain) {
return accessibleSymbolChain;
}
else if (
// If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols.
endOfChain ||
// If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.)
!(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) &&
// If a parent symbol is an anonymous type, don't write it.
!(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) {
return [symbol];
}
}
}
}
}

View file

@ -333,13 +333,18 @@ namespace ts {
return updateTypeOperatorNode(<TypeOperatorNode>node, visitNode((<TypeOperatorNode>node).type, visitor, isTypeNode));
case SyntaxKind.IndexedAccessType:
return updateIndexedAccessTypeNode((<IndexedAccessTypeNode>node)
, visitNode((<IndexedAccessTypeNode>node).objectType, visitor, isTypeNode)
, visitNode((<IndexedAccessTypeNode>node).indexType, visitor, isTypeNode));
, visitNode((<IndexedAccessTypeNode>node).objectType, visitor, isTypeNode)
, visitNode((<IndexedAccessTypeNode>node).indexType, visitor, isTypeNode));
case SyntaxKind.MappedType:
throw new Error("reached unsupported type in visitor.");
return updateMappedTypeNode((<MappedTypeNode>node)
, visitNode((<MappedTypeNode>node).readonlyToken, visitor, isToken)
, visitNode((<MappedTypeNode>node).typeParameter, visitor, isTypeParameter)
, visitNode((<MappedTypeNode>node).questionToken, visitor, isToken)
, visitNode((<MappedTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.LiteralType:
return updateLiteralTypeNode(<LiteralTypeNode>node
, visitNode((<LiteralTypeNode>node).literal, visitor, isExpression));
, visitNode((<LiteralTypeNode>node).literal, visitor, isExpression));
// Type Declarations

View file

@ -50,7 +50,6 @@ namespace ts.codefix {
, /*questionToken*/ undefined
, typeNode
, /*initializer*/ undefined);
// TODO: make index signature.
const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter });

View file

@ -4,7 +4,6 @@ namespace ts.codefix {
export function newNodesToChanges(newNodes: Node[], insertAfter: Node, context: CodeFixContext) {
const sourceFile = context.sourceFile;
if (!(newNodes)) {
// TODO: make the appropriate value flow through gracefully.
throw new Error("newNodesToChanges expects an array");
}
@ -13,6 +12,7 @@ namespace ts.codefix {
for (const newNode of newNodes) {
changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { suffix: context.newLineCharacter });
}
// TODO (aozgaa): concatenate changes into a single change.
return changeTracker.getChanges();
}
@ -51,7 +51,6 @@ namespace ts.codefix {
}
const declaration = declarations[0] as Declaration;
// TODO: get name as identifier or computer property name, etc.
const name = declaration.name ? getSynthesizedDeepClone(declaration.name) as PropertyName : undefined;
const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration));
const modifiers = visibilityModifier ? [visibilityModifier] : undefined;
@ -63,7 +62,6 @@ namespace ts.codefix {
case SyntaxKind.PropertySignature:
case SyntaxKind.PropertyDeclaration:
const typeNode = checker.createTypeNode(type, enclosingDeclaration);
// TODO: add modifiers.
const property = createProperty(
/*decorators*/undefined
, modifiers
@ -96,7 +94,6 @@ namespace ts.codefix {
let signatureDeclarations = [];
for (let i = 0; i < signatures.length; i++) {
// TODO: make signatures instead of methods
const signature = signatures[i];
const signatureParts = checker.createSignatureParts(signature, enclosingDeclaration);
signatureDeclarations.push(createMethod(

View file

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
//// interface I<X> {
//// x: keyof X;
//// }
//// class C<Y> implements I<Y> {[| |]}
verify.rangeAfterCodeFix(`
x: keyof Y;
`);

View file

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
//// interface I<X> {
//// x: { readonly [K in keyof X]: X[K] };
//// }
//// class C<Y> implements I<Y> {[| |]}
verify.rangeAfterCodeFix(`
x: { readonly [K in keyof X]: Y[K]; };
`);