Skip to content

Commit f0997c1

Browse files
committed
Merge branch 'master' of github.com:Microsoft/TypeScript
2 parents 8ce5811 + 424dba1 commit f0997c1

10 files changed

+78
-68
lines changed

src/services/codefixes/inferFromUsage.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ namespace ts.codefix {
3232

3333
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
3434
let declaration!: Declaration;
35-
const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken); });
35+
const changes = textChanges.ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeenseen*/ returnTrue); });
3636
return changes.length === 0 ? undefined
3737
: [createCodeFixAction(fixId, changes, [getDiagnostic(errorCode, token), getNameOfDeclaration(declaration).getText(sourceFile)], fixId, Diagnostics.Infer_all_types_from_usage)];
3838
},
3939
fixIds: [fixId],
4040
getAllCodeActions(context) {
4141
const { sourceFile, program, cancellationToken } = context;
42-
const seenFunctions = createMap<true>();
42+
const markSeen = nodeSeenTracker();
4343
return codeFixAll(context, errorCodes, (changes, err) => {
44-
doChange(changes, sourceFile, getTokenAtPosition(err.file!, err.start!, /*includeJsDocComment*/ false), err.code, program, cancellationToken, seenFunctions);
44+
doChange(changes, sourceFile, getTokenAtPosition(err.file!, err.start!, /*includeJsDocComment*/ false), err.code, program, cancellationToken, markSeen);
4545
});
4646
},
4747
});
@@ -57,7 +57,7 @@ namespace ts.codefix {
5757
}
5858
}
5959

60-
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, seenFunctions?: Map<true>): Declaration | undefined {
60+
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker): Declaration | undefined {
6161
if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken) {
6262
return undefined;
6363
}
@@ -67,18 +67,19 @@ namespace ts.codefix {
6767
// Variable and Property declarations
6868
case Diagnostics.Member_0_implicitly_has_an_1_type.code:
6969
case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code:
70-
if (isVariableDeclaration(parent) || isPropertyDeclaration(parent) || isPropertySignature(parent)) { // handle bad location
70+
if ((isVariableDeclaration(parent) && markSeen(parent)) || isPropertyDeclaration(parent) || isPropertySignature(parent)) { // handle bad location
7171
annotateVariableDeclaration(changes, sourceFile, parent, program, cancellationToken);
7272
return parent;
7373
}
7474
return undefined;
7575

7676
case Diagnostics.Variable_0_implicitly_has_an_1_type.code: {
7777
const symbol = program.getTypeChecker().getSymbolAtLocation(token);
78-
if (symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration)) {
78+
if (symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && markSeen(symbol.valueDeclaration)) {
7979
annotateVariableDeclaration(changes, sourceFile, symbol.valueDeclaration, program, cancellationToken);
8080
return symbol.valueDeclaration;
8181
}
82+
return undefined;
8283
}
8384
}
8485

@@ -96,7 +97,7 @@ namespace ts.codefix {
9697
}
9798
// falls through
9899
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
99-
if (!seenFunctions || addToSeen(seenFunctions, getNodeId(containingFunction))) {
100+
if (markSeen(containingFunction)) {
100101
const param = cast(parent, isParameter);
101102
annotateParameters(changes, param, containingFunction, sourceFile, program, cancellationToken);
102103
return param;
@@ -318,6 +319,16 @@ namespace ts.codefix {
318319
case SyntaxKind.ElementAccessExpression:
319320
inferTypeFromPropertyElementExpressionContext(<ElementAccessExpression>node.parent, node, checker, usageContext);
320321
break;
322+
case SyntaxKind.VariableDeclaration: {
323+
const { name, initializer } = node.parent as VariableDeclaration;
324+
if (node === name) {
325+
if (initializer) { // This can happen for `let x = null;` which still has an implicit-any error.
326+
addCandidateType(usageContext, checker.getTypeAtLocation(initializer));
327+
}
328+
break;
329+
}
330+
}
331+
// falls through
321332
default:
322333
return inferTypeFromContextualType(node, checker, usageContext);
323334
}
@@ -511,7 +522,7 @@ namespace ts.codefix {
511522
return checker.getStringType();
512523
}
513524
else if (usageContext.candidateTypes) {
514-
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), UnionReduction.Subtype));
525+
return checker.getWidenedType(checker.getUnionType(usageContext.candidateTypes.map(t => checker.getBaseTypeOfLiteralType(t)), UnionReduction.Subtype));
515526
}
516527
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) {
517528
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker);

src/services/refactors/generateGetAccessorAndSetAccessor.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
1717
fieldName: AcceptedNameType;
1818
accessorName: AcceptedNameType;
1919
originalName: AcceptedNameType;
20+
renameAccessor: boolean;
2021
}
2122

2223
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
@@ -43,7 +44,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
4344

4445
const isJS = isSourceFileJavaScript(file);
4546
const changeTracker = textChanges.ChangeTracker.fromContext(context);
46-
const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo;
47+
const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration, renameAccessor } = fieldInfo;
4748

4849
suppressLeadingAndTrailingTrivia(fieldName);
4950
suppressLeadingAndTrailingTrivia(declaration);
@@ -80,8 +81,10 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
8081

8182
const edits = changeTracker.getChanges();
8283
const renameFilename = file.fileName;
83-
const renameLocationOffset = isIdentifier(fieldName) ? 0 : -1;
84-
const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, fieldName.text, /*preferLastLocation*/ isParameter(declaration));
84+
85+
const nameNeedRename = renameAccessor ? accessorName : fieldName;
86+
const renameLocationOffset = isIdentifier(nameNeedRename) ? 0 : -1;
87+
const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, nameNeedRename.text, /*preferLastLocation*/ isParameter(declaration));
8588
return { renameFilename, renameLocation, edits };
8689
}
8790

@@ -110,15 +113,21 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
110113
return modifiers && createNodeArray(modifiers);
111114
}
112115

116+
function startsWithUnderscore(name: string): boolean {
117+
return name.charCodeAt(0) === CharacterCodes._;
118+
}
119+
113120
function getConvertibleFieldAtPosition(file: SourceFile, startPosition: number): Info | undefined {
114121
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
115122
const declaration = findAncestor(node.parent, isAcceptedDeclaration);
116123
// make sure declaration have AccessibilityModifier or Static Modifier or Readonly Modifier
117124
const meaning = ModifierFlags.AccessibilityModifier | ModifierFlags.Static | ModifierFlags.Readonly;
118125
if (!declaration || !isConvertableName(declaration.name) || (getModifierFlags(declaration) | meaning) !== meaning) return undefined;
119126

120-
const fieldName = createPropertyName(getUniqueName(`_${declaration.name.text}`, file.text), declaration.name);
121-
const accessorName = createPropertyName(declaration.name.text, declaration.name);
127+
const name = declaration.name.text;
128+
const startWithUnderscore = startsWithUnderscore(name);
129+
const fieldName = createPropertyName(startWithUnderscore ? name : getUniqueName(`_${name}`, file.text), declaration.name);
130+
const accessorName = createPropertyName(startWithUnderscore ? getUniqueName(name.substring(1), file.text) : name, declaration.name);
122131
return {
123132
isStatic: hasStaticModifier(declaration),
124133
isReadonly: hasReadonlyModifier(declaration),
@@ -128,6 +137,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
128137
declaration,
129138
fieldName,
130139
accessorName,
140+
renameAccessor: startWithUnderscore
131141
};
132142
}
133143

src/services/utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,8 @@ namespace ts {
11881188
}
11891189

11901190
/** Returns `true` the first time it encounters a node and `false` afterwards. */
1191-
export function nodeSeenTracker<T extends Node>(): (node: T) => boolean {
1191+
export type NodeSeenTracker<T = Node> = (node: T) => boolean;
1192+
export function nodeSeenTracker<T extends Node>(): NodeSeenTracker<T> {
11921193
const seen: true[] = [];
11931194
return node => {
11941195
const id = getNodeId(node);

tests/cases/fourslash/codeFixInferFromUsage_all.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// <reference path='fourslash.ts' />
22

33
// @noImplicitAny: true
4+
// @strictNullChecks: true
45

56
////function f(x, y) {
67
//// x += 0;
@@ -10,6 +11,11 @@
1011
////function g(z) {
1112
//// return z * 2;
1213
////}
14+
////
15+
////let x = null;
16+
////function h() {
17+
//// if (!x) x = 2;
18+
////}
1319

1420
verify.codeFixAll({
1521
fixId: "inferFromUsage",
@@ -22,5 +28,10 @@ verify.codeFixAll({
2228
2329
function g(z: number) {
2430
return z * 2;
31+
}
32+
33+
let x: number | null = null;
34+
function h() {
35+
if (!x) x = 2;
2536
}`,
2637
});

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess15.ts

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
//// class A {
44
//// /*a*/public _a: number = 1;/*b*/
5-
//// /*c*/public a: string = "foo";/*d*/
5+
//// public a: string = "foo";
66
//// }
77

88
goTo.select("a", "b");
@@ -11,36 +11,13 @@ edit.applyRefactor({
1111
actionName: "Generate 'get' and 'set' accessors",
1212
actionDescription: "Generate 'get' and 'set' accessors",
1313
newContent: `class A {
14-
private /*RENAME*/__a: number = 1;
15-
public get _a(): number {
16-
return this.__a;
14+
private _a: number = 1;
15+
public get /*RENAME*/a_1(): number {
16+
return this._a;
1717
}
18-
public set _a(value: number) {
19-
this.__a = value;
18+
public set a_1(value: number) {
19+
this._a = value;
2020
}
2121
public a: string = "foo";
2222
}`,
2323
});
24-
25-
goTo.select("c", "d");
26-
edit.applyRefactor({
27-
refactorName: "Generate 'get' and 'set' accessors",
28-
actionName: "Generate 'get' and 'set' accessors",
29-
actionDescription: "Generate 'get' and 'set' accessors",
30-
newContent: `class A {
31-
private __a: number = 1;
32-
public get _a(): number {
33-
return this.__a;
34-
}
35-
public set _a(value: number) {
36-
this.__a = value;
37-
}
38-
private /*RENAME*/_a_1: string = "foo";
39-
public get a(): string {
40-
return this._a_1;
41-
}
42-
public set a(value: string) {
43-
this._a_1 = value;
44-
}
45-
}`,
46-
});

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess23.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ edit.applyRefactor({
2828
actionName: "Generate 'get' and 'set' accessors",
2929
actionDescription: "Generate 'get' and 'set' accessors",
3030
newContent: `class A {
31-
private /*RENAME*/__a: number = 1;
32-
public get _a(): number {
33-
return this.__a;
31+
private _a: number = 1;
32+
public get /*RENAME*/a_2(): number {
33+
return this._a;
3434
}
35-
public set _a(value: number) {
36-
this.__a = value;
35+
public set a_2(value: number) {
36+
this._a = value;
3737
}
3838
private _a_1: string = "foo";
3939
public get a(): string {

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess4.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ edit.applyRefactor({
1010
actionName: "Generate 'get' and 'set' accessors",
1111
actionDescription: "Generate 'get' and 'set' accessors",
1212
newContent: `class A {
13-
private /*RENAME*/__a: string;
14-
public get _a(): string {
15-
return this.__a;
13+
private _a: string;
14+
public get /*RENAME*/a_1(): string {
15+
return this._a;
1616
}
17-
public set _a(value: string) {
18-
this.__a = value;
17+
public set a_1(value: string) {
18+
this._a = value;
1919
}
2020
}`,
2121
});

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess5.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ edit.applyRefactor({
1010
actionName: "Generate 'get' and 'set' accessors",
1111
actionDescription: "Generate 'get' and 'set' accessors",
1212
newContent: `class A {
13-
private /*RENAME*/__a: string;
14-
public get _a(): string {
15-
return this.__a;
13+
private _a: string;
14+
public get /*RENAME*/a_1(): string {
15+
return this._a;
1616
}
17-
public set _a(value: string) {
18-
this.__a = value;
17+
public set a_1(value: string) {
18+
this._a = value;
1919
}
2020
}`,
2121
});

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess6.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ edit.applyRefactor({
1010
actionName: "Generate 'get' and 'set' accessors",
1111
actionDescription: "Generate 'get' and 'set' accessors",
1212
newContent: `class A {
13-
private /*RENAME*/__a: string;
14-
public get _a(): string {
15-
return this.__a;
13+
private _a: string;
14+
public get /*RENAME*/a_1(): string {
15+
return this._a;
1616
}
17-
public set _a(value: string) {
18-
this.__a = value;
17+
public set a_1(value: string) {
18+
this._a = value;
1919
}
2020
}`,
2121
});

tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess7.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ edit.applyRefactor({
1010
actionName: "Generate 'get' and 'set' accessors",
1111
actionDescription: "Generate 'get' and 'set' accessors",
1212
newContent: `class A {
13-
private /*RENAME*/__a: string;
14-
protected get _a(): string {
15-
return this.__a;
13+
private _a: string;
14+
protected get /*RENAME*/a_1(): string {
15+
return this._a;
1616
}
17-
protected set _a(value: string) {
18-
this.__a = value;
17+
protected set a_1(value: string) {
18+
this._a = value;
1919
}
2020
}`,
2121
});

0 commit comments

Comments
 (0)