-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation of Extract Constant #18783
Conversation
Major changes: 1) Instead of skipping undesirable scopes, include them and mark them with errors. Constants can be extracted into more scopes. 2) Update the tests to call through the "public" API. This caused some baseline changes. 3) Rename refactoring to "Extract Symbol" for generality. 4) Return a second ApplicableRefactorInfo for constants. Distinguish the two by splitting the action name.
@@ -1,823 +0,0 @@ | |||
/// <reference path="..\harness.ts" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't detected as a rename because large chunks of it were pulled out into extractRanges.ts and extractSymbolTestHelpers.ts.
/// <reference path="extractTestHelpers.ts" /> | ||
|
||
namespace ts { | ||
describe("extractConstants", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extractConstants [](start = 14, length = 16)
This test coverage isn't great because I didn't want to revise everything as soon as I updated the insertion locations.
} | ||
`, | ||
[ | ||
"Cannot extract range containing conditional break or continue statements." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we symbolically refer to these strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly. This was all copied from the old extractMethods.ts.
In reply to: 141221847 [](ancestors = 141221847)
const extractMethod: Refactor = { | ||
name: "Extract Method", | ||
description: Diagnostics.Extract_function.message, | ||
namespace ts.refactor.extractSymbol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extractSymbol [](start = 22, length = 13)
@RyanCavanaugh, the interesting changes are here.
namespace A { | ||
export interface I { x: number }; | ||
class C { | ||
a() { | ||
let z = 1; | ||
return this./*RENAME*/newFunction(); | ||
return this./*RENAME*/newMethod(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newMethod [](start = 34, length = 9)
Slightly non-trivial baseline update
refactorName: "Extract Method", | ||
actionName: "scope_0", | ||
refactorName: "Extract Symbol", | ||
actionName: "function_scope_1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 [](start = 32, length = 1)
0 is now the method itself. We won't insert a function there, but we will insert a local.
const visibleDeclarationsInExtractedRange: Symbol[] = []; | ||
|
||
const expressionDiagnostics = | ||
isReadonlyArray(targetRange.range) && !(targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use "extract local" on this expression
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On which expression? targetRange.range
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything to the left of the ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That breaks control flow type checking.
isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration | ||
? [createDiagnosticForNode(scope, Messages.CannotExtractToOtherFunctionLike)] | ||
: []); | ||
constantErrorsPerScope.push(expressionDiagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The re-use of the same array object going into multiple positions in constantErrorsPerScope
makes me nervous since this is currently mutable - let's change both to ReadonlyArray<Diagnostic>[]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading further down the function, I'm now confused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.slice()
break; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would assert prevChild !== undefined
here since it's not obvious by construction that this is impossible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but it follows from the fact that extraction position is in a child that begins before the extraction position (i.e. there will be at least one iteration and that iteration won't kick out based on the position).
// Confirm that the constraint is preserved. | ||
testExtractMethod("extractMethod15", | ||
`function F<T>(t1: T) { | ||
function G<U extends T[]>(t2: U) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was it a test correctness thing to change F
to G
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We de-dup actions by description and I wanted to see all of them.
Testcase we were looking at /*!
* MIT APACHE
*/
"use strict";
"use module";
namespace Y { }
namespace X {
namespace Y {
export const j = 10;
namespace Z {
export const y = j * j;
}
}
} as well as
and
|
I can repro the assert in a unit test. This looks relevant: https://github.com/Microsoft/TypeScript/pull/18484/files#diff-20a9fa4d3e9d09f60c275a3dda245867 |
@RyanCavanaugh The problem we were seeing (in our case, an assert about token spans) will occur any time a substitution is applied more than once. Since the same issue affects Extract Function, I'm going to handle it in a separate PR. |
@RyanCavanaugh I believe all outstanding issues can be addressed in subsequent PRs. |
Many fixes to follow. 😄 |
[First, I propose standardizing on the terminology "extract function" and "extract constant", which, I argue, are more accurate than "extract method" and "extract local". This has no impact on the user-facing strings.]
This change generalizes the Extract Function refactoring (formerly, "Extract Method") to Extract Symbol and has it return both function and constant extractions. This required adding consideration for additional scopes into which it's sensible to insert constants but not functions. For Extract Function, the new scopes are immediately marked with errors.
This change also switches the tests to use the "public" API so that the two families of extractions can be distinguished. The baselines have been updated accordingly.
Outstanding work: