Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthur Ozga committed Feb 16, 2017
1 parent d9e0fff commit a4cf12e
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 102 deletions.
2 changes: 1 addition & 1 deletion src/harness/fourslash.ts
Expand Up @@ -2205,7 +2205,7 @@ namespace FourSlash {
this.raiseError(`Should find at least ${index + 1} codefix(es), but ${actions ? actions.length : "none"} found.`);
}
}

const fileChanges = ts.find(actions[index].changes, change => change.fileName === fileName);
if (!fileChanges) {
this.raiseError("The CodeFix found doesn't provide any changes in this file.");
Expand Down
163 changes: 66 additions & 97 deletions src/services/codefixes/fixAddMissingMember.ts
@@ -1,98 +1,67 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
getCodeActions: getActionsForAddMissingMember
});

function getActionsForAddMissingMember(context: CodeFixContext): CodeAction[] | undefined {

const sourceFile = context.sourceFile;
const start = context.span.start;
// This is the identifier in the case of a class declaration
// or the class keyword token in the case of a class expression.
const token = getTokenAtPosition(sourceFile, start);

const classDeclaration = getContainingClass(token);
if (!classDeclaration) {
return undefined;
}

const startPos = classDeclaration.members.pos;

if (!(token.parent && token.parent.kind === SyntaxKind.PropertyAccessExpression)) {
return undefined;
}

if ((token.parent as PropertyAccessExpression).expression.kind !== SyntaxKind.ThisKeyword) {
return undefined;
}

// if function call, synthesize function declaration
if(token.parent.parent.kind == SyntaxKind.CallExpression) {
const callExpression = token.parent.parent as CallExpression;
if(callExpression.typeArguments) {
/**
* We can't in general know which arguments should use the type of the expression
* or the type of the type argument in the declaration. Consider
* ```
* class A {
* constructor(a: number){
* this.foo<number>(a,1,true);
* }
* }
* ```
*/
return undefined;
}


}

let typeString: string = 'any';

// if binary expression, try to infer type for LHS, else use any
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
const binaryExpression = token.parent.parent as BinaryExpression;
binaryExpression.operatorToken;

const checker = context.program.getTypeChecker();
const widenedType = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right));
typeString = checker.typeToString(widenedType);
}

return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `${token.getFullText(sourceFile)}: ${typeString};`
}]
}]
},
{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_accessor_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `[name: string]: ${typeString};`
}]
}]
}];
}

// Want to infer type of x when possible. ie:
// * assignment,
// * function call argument: foo<T>(this.x) where foo(x: SomeType<T>)
// * expression with a type assertion: this.x as MyFavoriteType
// * access expression: this.x.push("asdf") ... probably an array?
// *
// What if there are multiple usages of this.x? Create intersection over all usages?

// needs to be in a class
// inferred type might be error. then add any.
// either make indexable of the inferred type
// add named member of the inferred type.
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
getCodeActions: getActionsForAddMissingMember
});

function getActionsForAddMissingMember(context: CodeFixContext): CodeAction[] | undefined {

const sourceFile = context.sourceFile;
const start = context.span.start;
// This is the identifier of the missing property. eg:
// this.missing = 1;
// ^^^^^^^
const token = getTokenAtPosition(sourceFile, start);

if (token.kind != SyntaxKind.Identifier) {
return undefined;
}

const classDeclaration = getContainingClass(token);
if (!classDeclaration) {
return undefined;
}

if (!(token.parent && token.parent.kind === SyntaxKind.PropertyAccessExpression)) {
return undefined;
}

if ((token.parent as PropertyAccessExpression).expression.kind !== SyntaxKind.ThisKeyword) {
return undefined;
}

let typeString = "any";

if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
const binaryExpression = token.parent.parent as BinaryExpression;

const checker = context.program.getTypeChecker();
const widenedType = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right));
typeString = checker.typeToString(widenedType);
}

const startPos = classDeclaration.members.pos;

return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `${token.getFullText(sourceFile)}: ${typeString};`
}]
}]
},
{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_accessor_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `[name: string]: ${typeString};`
}]
}]
}];
}
}
8 changes: 4 additions & 4 deletions src/services/codefixes/helpers.ts
Expand Up @@ -58,7 +58,7 @@ namespace ts.codefix {
if (declarations.length === 1) {
Debug.assert(signatures.length === 1);
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
return getStubbedMethod(visibility, name, sigString, newlineChar);
}

let result = "";
Expand All @@ -78,7 +78,7 @@ namespace ts.codefix {
bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker);
}
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
result += getStubbedMethod(visibility, name, sigString, newlineChar);

return result;
default:
Expand Down Expand Up @@ -138,8 +138,8 @@ namespace ts.codefix {
}
}

export function getStubbedMethod(visibility: string, name: string, signature: string = '()', newlineChar: string): string {
return `${visibility}${name}${signature}${getMethodBodyStub(newlineChar)}`;
export function getStubbedMethod(visibility: string, name: string, sigString = "()", newlineChar: string): string {
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
}

function getMethodBodyStub(newlineChar: string) {
Expand Down

0 comments on commit a4cf12e

Please sign in to comment.