@@ -8,116 +8,151 @@ namespace ts.codefix {
8
8
9
9
function getActionsForAddMissingMember ( context : CodeFixContext ) : CodeAction [ ] | undefined {
10
10
11
- const sourceFile = context . sourceFile ;
11
+ const tokenSourceFile = context . sourceFile ;
12
12
const start = context . span . start ;
13
- // This is the identifier of the missing property. eg:
13
+ // The identifier of the missing property. eg:
14
14
// this.missing = 1;
15
15
// ^^^^^^^
16
- const token = getTokenAtPosition ( sourceFile , start , /*includeJsDocComment*/ false ) ;
16
+ const token = getTokenAtPosition ( tokenSourceFile , start , /*includeJsDocComment*/ false ) ;
17
17
18
18
if ( token . kind !== SyntaxKind . Identifier ) {
19
19
return undefined ;
20
20
}
21
21
22
- if ( ! isPropertyAccessExpression ( token . parent ) || token . parent . expression . kind !== SyntaxKind . ThisKeyword ) {
22
+ if ( ! isPropertyAccessExpression ( token . parent ) ) {
23
23
return undefined ;
24
24
}
25
25
26
- const classMemberDeclaration = getThisContainer ( token , /*includeArrowFunctions*/ false ) ;
27
- if ( ! isClassElement ( classMemberDeclaration ) ) {
28
- return undefined ;
26
+ const tokenName = token . getText ( tokenSourceFile ) ;
27
+
28
+ let makeStatic = false ;
29
+ let classDeclaration : ClassLikeDeclaration ;
30
+
31
+ if ( token . parent . expression . kind === SyntaxKind . ThisKeyword ) {
32
+ const containingClassMemberDeclaration = getThisContainer ( token , /*includeArrowFunctions*/ false ) ;
33
+ if ( ! isClassElement ( containingClassMemberDeclaration ) ) {
34
+ return undefined ;
35
+ }
36
+
37
+ classDeclaration = < ClassLikeDeclaration > containingClassMemberDeclaration . parent ;
38
+
39
+ // Property accesses on `this` in a static method are accesses of a static member.
40
+ makeStatic = classDeclaration && hasModifier ( containingClassMemberDeclaration , ModifierFlags . Static ) ;
41
+ }
42
+ else {
43
+
44
+ const checker = context . program . getTypeChecker ( ) ;
45
+ const leftExpression = token . parent . expression ;
46
+ const leftExpressionType = checker . getTypeAtLocation ( leftExpression ) ;
47
+
48
+ if ( leftExpressionType . flags & TypeFlags . Object ) {
49
+ const symbol = leftExpressionType . symbol ;
50
+ if ( symbol . flags & SymbolFlags . Class ) {
51
+ classDeclaration = symbol . declarations && < ClassLikeDeclaration > symbol . declarations [ 0 ] ;
52
+ if ( getObjectFlags ( leftExpressionType ) & ObjectFlags . Anonymous && symbol . flags & SymbolFlags . Class && ! checker . getBaseTypeVariableOfClass ( symbol ) ) {
53
+ makeStatic = true ;
54
+ }
55
+ }
56
+ }
29
57
}
30
58
31
- const classDeclaration = < ClassLikeDeclaration > classMemberDeclaration . parent ;
32
59
if ( ! classDeclaration || ! isClassLike ( classDeclaration ) ) {
33
60
return undefined ;
34
61
}
35
62
36
- const tokenName = token . getText ( sourceFile ) ;
37
- const isStatic = hasModifier ( classMemberDeclaration , ModifierFlags . Static ) ;
63
+ const classDeclarationSourceFile = getSourceFileOfNode ( classDeclaration ) ;
64
+ const classOpenBrace = getOpenBraceOfClassLike ( classDeclaration , classDeclarationSourceFile ) ;
38
65
39
- return isInJavaScriptFile ( sourceFile ) ? getActionsForAddMissingMemberInJavaScriptFile ( ) : getActionsForAddMissingMemberInTypeScriptFile ( ) ;
66
+ return isInJavaScriptFile ( classDeclarationSourceFile ) ?
67
+ getActionsForAddMissingMemberInJavaScriptFile ( classDeclaration , makeStatic ) :
68
+ getActionsForAddMissingMemberInTypeScriptFile ( classDeclaration , makeStatic ) ;
69
+
70
+ function getActionsForAddMissingMemberInJavaScriptFile ( classDeclaration : ClassLikeDeclaration , makeStatic : boolean ) : CodeAction [ ] | undefined {
71
+ let actions : CodeAction [ ] ;
40
72
41
- function getActionsForAddMissingMemberInJavaScriptFile ( ) : CodeAction [ ] | undefined {
42
- if ( isStatic ) {
73
+ const methodCodeAction = getActionForMethodDeclaration ( ) ;
74
+ if ( methodCodeAction ) {
75
+ actions = [ methodCodeAction ] ;
76
+ }
77
+
78
+ if ( makeStatic ) {
43
79
if ( classDeclaration . kind === SyntaxKind . ClassExpression ) {
44
- return undefined ;
80
+ return actions ;
45
81
}
46
82
47
83
const className = classDeclaration . name . getText ( ) ;
48
84
49
- return [ {
85
+ const initializeStaticAction = {
50
86
description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Initialize_static_property_0 ) , [ tokenName ] ) ,
51
87
changes : [ {
52
- fileName : sourceFile . fileName ,
88
+ fileName : classDeclarationSourceFile . fileName ,
53
89
textChanges : [ {
54
90
span : { start : classDeclaration . getEnd ( ) , length : 0 } ,
55
91
newText : `${ context . newLineCharacter } ${ className } .${ tokenName } = undefined;${ context . newLineCharacter } `
56
92
} ]
57
93
} ]
58
- } ] ;
94
+ } ;
59
95
96
+ ( actions || ( actions = [ ] ) ) . push ( initializeStaticAction ) ;
97
+ return actions ;
60
98
}
61
99
else {
62
100
const classConstructor = getFirstConstructorWithBody ( classDeclaration ) ;
63
101
if ( ! classConstructor ) {
64
- return undefined ;
102
+ return actions ;
65
103
}
66
104
67
- return [ {
105
+ const initializeAction = {
68
106
description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Initialize_property_0_in_the_constructor ) , [ tokenName ] ) ,
69
107
changes : [ {
70
- fileName : sourceFile . fileName ,
108
+ fileName : classDeclarationSourceFile . fileName ,
71
109
textChanges : [ {
72
110
span : { start : classConstructor . body . getEnd ( ) - 1 , length : 0 } ,
73
111
newText : `this.${ tokenName } = undefined;${ context . newLineCharacter } `
74
112
} ]
75
113
} ]
76
- } ] ;
114
+ } ;
115
+
116
+ ( actions || ( actions = [ ] ) ) . push ( initializeAction ) ;
117
+ return actions ;
77
118
}
78
119
}
79
120
80
- function getActionsForAddMissingMemberInTypeScriptFile ( ) : CodeAction [ ] | undefined {
81
- const openBrace = getOpenBraceOfClassLike ( classDeclaration , sourceFile ) ;
121
+ function getActionsForAddMissingMemberInTypeScriptFile ( classDeclaration : ClassLikeDeclaration , makeStatic : boolean ) : CodeAction [ ] | undefined {
82
122
let actions : CodeAction [ ] ;
83
123
84
- if ( token . parent . parent . kind === SyntaxKind . CallExpression ) {
85
- const callExpression = < CallExpression > token . parent . parent ;
86
- const methodDeclaration = createMethodFromCallExpression ( callExpression , tokenName ) ;
87
-
88
- const methodDeclarationChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
89
- methodDeclarationChangeTracker . insertNodeAfter ( sourceFile , openBrace , methodDeclaration , { suffix : context . newLineCharacter } ) ;
90
- actions = [ {
91
- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_method_0 ) , [ tokenName ] ) ,
92
- changes : methodDeclarationChangeTracker . getChanges ( )
93
- } ] ;
124
+ const methodCodeAction = getActionForMethodDeclaration ( ) ;
125
+ if ( methodCodeAction ) {
126
+ actions = [ methodCodeAction ] ;
94
127
}
95
128
96
129
let typeNode : TypeNode ;
97
130
if ( token . parent . parent . kind === SyntaxKind . BinaryExpression ) {
98
131
const binaryExpression = token . parent . parent as BinaryExpression ;
132
+ const otherExpression = token . parent === binaryExpression . left ? binaryExpression . right : binaryExpression . left ;
99
133
const checker = context . program . getTypeChecker ( ) ;
100
- const widenedType = checker . getWidenedType ( checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( binaryExpression . right ) ) ) ;
134
+ const widenedType = checker . getWidenedType ( checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( otherExpression ) ) ) ;
101
135
typeNode = checker . typeToTypeNode ( widenedType , classDeclaration ) ;
102
136
}
103
137
typeNode = typeNode || createKeywordTypeNode ( SyntaxKind . AnyKeyword ) ;
104
138
105
139
const property = createProperty (
106
140
/*decorators*/ undefined ,
107
- /*modifiers*/ isStatic ? [ createToken ( SyntaxKind . StaticKeyword ) ] : undefined ,
141
+ /*modifiers*/ makeStatic ? [ createToken ( SyntaxKind . StaticKeyword ) ] : undefined ,
108
142
tokenName ,
109
143
/*questionToken*/ undefined ,
110
144
typeNode ,
111
145
/*initializer*/ undefined ) ;
112
146
const propertyChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
113
- propertyChangeTracker . insertNodeAfter ( sourceFile , openBrace , property , { suffix : context . newLineCharacter } ) ;
147
+ propertyChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , property , { suffix : context . newLineCharacter } ) ;
114
148
115
149
( actions || ( actions = [ ] ) ) . push ( {
116
- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_property_0 ) , [ tokenName ] ) ,
150
+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Declare_property_0 ) , [ tokenName ] ) ,
117
151
changes : propertyChangeTracker . getChanges ( )
118
152
} ) ;
119
153
120
- if ( ! isStatic ) {
154
+ if ( ! makeStatic ) {
155
+ // Index signatures cannot have the static modifier.
121
156
const stringTypeNode = createKeywordTypeNode ( SyntaxKind . StringKeyword ) ;
122
157
const indexingParameter = createParameter (
123
158
/*decorators*/ undefined ,
@@ -134,15 +169,32 @@ namespace ts.codefix {
134
169
typeNode ) ;
135
170
136
171
const indexSignatureChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
137
- indexSignatureChangeTracker . insertNodeAfter ( sourceFile , openBrace , indexSignature , { suffix : context . newLineCharacter } ) ;
172
+ indexSignatureChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , indexSignature , { suffix : context . newLineCharacter } ) ;
138
173
139
174
actions . push ( {
140
- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_signature_for_missing_property_0 ) , [ tokenName ] ) ,
175
+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_signature_for_property_0 ) , [ tokenName ] ) ,
141
176
changes : indexSignatureChangeTracker . getChanges ( )
142
177
} ) ;
143
178
}
144
179
145
180
return actions ;
146
181
}
182
+
183
+ function getActionForMethodDeclaration ( ) : CodeAction | undefined {
184
+ if ( token . parent . parent . kind === SyntaxKind . CallExpression ) {
185
+ const callExpression = < CallExpression > token . parent . parent ;
186
+ const methodDeclaration = createMethodFromCallExpression ( callExpression , tokenName , /*includeTypeScriptSyntax*/ true , makeStatic ) ;
187
+
188
+ const methodDeclarationChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
189
+ methodDeclarationChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , methodDeclaration , { suffix : context . newLineCharacter } ) ;
190
+ return {
191
+ description : formatStringFromArgs ( getLocaleSpecificMessage ( makeStatic ?
192
+ Diagnostics . Declare_method_0 :
193
+ Diagnostics . Declare_static_method_0 ) ,
194
+ [ tokenName ] ) ,
195
+ changes : methodDeclarationChangeTracker . getChanges ( )
196
+ } ;
197
+ }
198
+ }
147
199
}
148
200
}
0 commit comments