@@ -4,9 +4,12 @@ import {
4
4
Diagnostics ,
5
5
emptyArray ,
6
6
Expression ,
7
+ factory ,
7
8
FindAllReferences ,
9
+ getExpressionPrecedence ,
8
10
getLocaleSpecificMessage ,
9
11
getTokenAtPosition ,
12
+ isExpression ,
10
13
isIdentifier ,
11
14
isInitializedVariable ,
12
15
isVariableDeclarationInVariableStatement ,
@@ -98,7 +101,7 @@ registerRefactor(refactorName, {
98
101
const { references, declaration, replacement } = info ;
99
102
const edits = textChanges . ChangeTracker . with ( context , tracker => {
100
103
for ( const node of references ) {
101
- tracker . replaceNode ( file , node , replacement ) ;
104
+ tracker . replaceNode ( file , node , getReplacementExpression ( node , replacement ) ) ;
102
105
}
103
106
tracker . delete ( file , declaration ) ;
104
107
} ) ;
@@ -149,7 +152,7 @@ function getInliningInfo(file: SourceFile, startPosition: number, tryWithReferen
149
152
return { error : getLocaleSpecificMessage ( Diagnostics . Could_not_find_variable_to_inline ) } ;
150
153
}
151
154
152
- function getReferenceNodes ( entries : readonly FindAllReferences . Entry [ ] , declaration : Node ) {
155
+ function getReferenceNodes ( entries : readonly FindAllReferences . Entry [ ] , declaration : Node ) : Node [ ] | undefined {
153
156
const referenceNodes = mapDefined ( entries , entry => {
154
157
// Only replace node references, and exclude the original variable too.
155
158
if ( entry . kind !== FindAllReferences . EntryKind . Node || entry . node === declaration ) {
@@ -168,4 +171,22 @@ function getReferenceNodes(entries: readonly FindAllReferences.Entry[], declarat
168
171
// Return undefined if the only reference is the declaration itself, or if a reference
169
172
// isn't applicable for inlining.
170
173
return referenceNodes . length > 0 && referenceNodes . length === entries . length - 1 ? referenceNodes : undefined ;
174
+ }
175
+
176
+ function getReplacementExpression ( reference : Node , replacement : Expression ) : Expression {
177
+ // Logic from binaryOperandNeedsParentheses: "If the operand has lower precedence,
178
+ // then it needs to be parenthesized to preserve the intent of the expression.
179
+ // If the operand has higher precedence, then it does not need to be parenthesized."
180
+ //
181
+ // Note that binaryOperandNeedsParentheses has further logic when the precedences
182
+ // are equal, but for the purposes of this refactor we keep things simple and always
183
+ // parenthesize.
184
+ const { parent } = reference ;
185
+ if ( isExpression ( parent ) ) {
186
+ if ( getExpressionPrecedence ( replacement ) <= getExpressionPrecedence ( parent ) ) {
187
+ return factory . createParenthesizedExpression ( replacement ) ;
188
+ }
189
+ }
190
+
191
+ return replacement ;
171
192
}
0 commit comments