Repository navigation
Add a refactoring between reference and struct tuples - #20548
xperiandri wants to merge 10 commits into
Conversation
✅ Release notes checked
|
fb6c03d to
512e8cb
Compare
This comment has been minimized.
This comment has been minimized.
Covered casesConverts
Not offered
|
T-Gro
left a comment
There was a problem hiding this comment.
🤖 🕵️ AI review — verify independently.
| /// Whether the tuple is the argument list of a method, constructor or union case call rather than a tuple value. | ||
| let isArgumentList (tuple: SynExpr) (path: SyntaxVisitorPath) = | ||
| match path with | ||
| | SyntaxNode.SynExpr(SynExpr.Paren(expr = inner) as paren) :: SyntaxNode.SynExpr(SynExpr.App(flag = ExprAtomicFlag.Atomic; argExpr = arg) | SynExpr.New( |
There was a problem hiding this comment.
🤖 🕵️ [P2] Converting 1, 2 passes one struct-tuple argument to Math.Max — valid method calls with whitespace before ( stop compiling.
module M
let biggest = System.Math.Max (1, 2)
// Convert "1, 2" to a struct tuple.
// Result: System.Math.Max struct (1, 2)There was a problem hiding this comment.
Fixed in 4cfee1f: a parenthesized tuple is an argument list when the applied name resolves to a method or constructor with one argument list (or a union case of several fields), whatever the spacing. Test: No action with System.Math.Max (1, 2) and Calc().Add (1, 2).
| this.Retarget source result (SyntaxNode.SynMatchClause clause :: childPath) | ||
| | SynExpr.Sequential(expr2 = last) -> this.Retarget source last childPath | ||
| | SynExpr.LetOrUse letOrUse -> this.Retarget source letOrUse.Body childPath | ||
| | _ -> () |
There was a problem hiding this comment.
🤖 🕵️ [P2] Converting (x, y) leaves make returning a reference tuple through try/finally — the converted pattern fails to typecheck.
module M
let make () =
try (1, 2)
finally ()
let (x, y) = make ()
// Convert "x, y" to a struct tuple.There was a problem hiding this comment.
Fixed in 4cfee1f: results now flow into and out of the try body of try/finally, and the body and handlers of try/with. Test: Result flowing out of try converts every branch of it.
|
|
||
| if isWholeAnnotation || isGenericArgument sourceText keyword.Start span.End then | ||
| [ | ||
| TextChange(TextSpan(keyword.Start, keyword.Length + 1), "") |
There was a problem hiding this comment.
🤖 🕵️ [P2] Converting this type to a reference tuple deletes the comment's opening ( — the result is invalid syntax.
module M
type R = { Pair: struct (* note *) (int * int) }
// Convert "int * int" to a reference tuple.
// Result: type R = { Pair: * note *) (int * int }There was a problem hiding this comment.
Fixed in 4cfee1f: only struct and the blanks after it are removed, and the tuple's ( is found past any comments. Test: Comment between struct and its parenthesis is kept.
| /// `struct ` to insert in front of the `(` at the position, after a space when it would run into a name: `f(a, b)`. | ||
| let private structAt (sourceText: SourceText) (position: int) = | ||
| match if position > 0 then sourceText[position - 1] else ' ' with | ||
| | c when Char.IsLetterOrDigit c || c = '_' || c = '\'' || c = '`' || c = ')' || c = ']' -> " struct " |
There was a problem hiding this comment.
🤖 🕵️ [P2] Converting (a,b) appends struct to the function identifier ending in U+0301 — its callers fail with an undefined-name error.
module M
let f́(a,b) = a+b
let x = f́ (1,2)
// Convert "a,b" to a struct tuple.
// Result: let f́struct (a,b) = a+bThere was a problem hiding this comment.
Fixed in 4cfee1f: the check for a preceding name now uses the lexer's identifier character categories (letters, Nl, Nd, Pc, Mn, Mc, Cf, '). Test: Struct keyword is separated from a name ending in a combining mark.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Compaction is pushed as 2a032ce (sources) and e32822e (tests). The new comment lines in the diff go from 74 to 5 (6558 to 597 chars): three license headers, one note on a parser quirk and one on the synthetic-project checker; the rest is named instead, for example
Behaviour is unchanged: |
T-Gro
left a comment
There was a problem hiding this comment.
🤖🕵️ AI review — verify independently.
| | SynPat.Named(ident = SynIdent(ident, _)) | ||
| | SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ]); argPats = SynArgPats.Pats []) -> this.EnqueueValue source ident | ||
| | SynPat.Tuple _ when not (isParameterList pat path) -> this.AddOrFail source (tryPatChanges source.Text toStruct pat path) | ||
| | _ -> () |
There was a problem hiding this comment.
🤖🕵️ Conversion through an as pattern updates the value but not both patterns; FS0001 follows.
let pair = (1, 2)
let ((a, b) as alias) = pairCtrl+. on a tuple expression, pattern or annotated tuple type converts it to the other kind and follows the value through the solution. From dotnet#20548 (release note left out: 18.vNext.md has local edits). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548: Find All References did not see the second document added to a single-file test solution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ents with their calls From dotnet#20548 and dotnet#20549. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…et#20549) up to their pull request heads Their calls to SymbolHelpers.getSymbolUses ask for RelatedSymbolUseKind.AllInCode, as the XML doc rename work on this branch requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…annotated patterns The conversion now also follows a tuple through - a function in parentheses, `(sum) (1, 2)`; - a name bound to a lambda, its parameters and its result; - a function alias and a partial application, `let alias = make`; - a named argument, `M(p = pair)`; - an annotated tuple pattern, `((a, b): int * int)`, from the pattern, the annotation or the value; - a value whose binding head is in parentheses, `let (pair: int * int) = (1, 2)`. It is no longer offered where the result cannot compile or nothing would change: - on the annotation of a member's only argument, which is its parameter list; - on the argument of a union case whose only field is declared a tuple; - on the argument of a method in parentheses, `(c.Add) (1, 2)`; - on a tuple over several lines without parentheses, which cannot change in place. `struct` is kept apart from type arguments the parenthesis follows: `f<int> struct (1, 2)`. This is b7bc303 of dotnet#20548 as dotnet#20549 merged it (3abaa70) into the engine both kinds share: `StructConversion.fs` and `StructPropagation.fs` take what went into `TupleConversion.fs` and `TuplePropagation.fs` there. A member's parameter list is told by the kind, so the annotation of a member's only argument stays convertible for an anonymous record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of the dotnet#20549 compaction (feature/anonymous-record-struct-refactoring 4c7066a..5be006c) to all-fixes. The range includes the pull request's merge of the compacted tuple refactoring (dotnet#20548), so this one commit covers both pull requests: all-fixes has the generalized engine of dotnet#20549 (StructPropagation.fs, TupleConversion.fs as a kind of that engine) and no TuplePropagation.fs, and the merge kept that layout. - dotnet#20548: Compact the tuple refactoring sources - dotnet#20548: Share the cross-file refactoring test setup - dotnet#20549: Compact the shared struct conversion sources - dotnet#20549: Share the cross-file test setup of the conversion tests StructPropagation.fs reads the related uses once now (ForEachUse) and keeps the RelatedSymbolUseKind.AllInCode argument that all-fixes needs there. RefactorTestFramework.fs merges the pull request's helpers with those of the Extract suites that the earlier commits of this series put there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ctrl+. on a tuple expression, pattern or annotated tuple type converts it to the other kind and follows the value through the solution. From dotnet#20548 (release note left out: 18.vNext.md has local edits). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548: Find All References did not see the second document added to a single-file test solution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ents with their calls From dotnet#20548 and dotnet#20549. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…et#20549) up to their pull request heads Their calls to SymbolHelpers.getSymbolUses ask for RelatedSymbolUseKind.AllInCode, as the XML doc rename work on this branch requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…annotated patterns The conversion now also follows a tuple through - a function in parentheses, `(sum) (1, 2)`; - a name bound to a lambda, its parameters and its result; - a function alias and a partial application, `let alias = make`; - a named argument, `M(p = pair)`; - an annotated tuple pattern, `((a, b): int * int)`, from the pattern, the annotation or the value; - a value whose binding head is in parentheses, `let (pair: int * int) = (1, 2)`. It is no longer offered where the result cannot compile or nothing would change: - on the annotation of a member's only argument, which is its parameter list; - on the argument of a union case whose only field is declared a tuple; - on the argument of a method in parentheses, `(c.Add) (1, 2)`; - on a tuple over several lines without parentheses, which cannot change in place. `struct` is kept apart from type arguments the parenthesis follows: `f<int> struct (1, 2)`. This is b7bc303 of dotnet#20548 as dotnet#20549 merged it (3abaa70) into the engine both kinds share: `StructConversion.fs` and `StructPropagation.fs` take what went into `TupleConversion.fs` and `TuplePropagation.fs` there. A member's parameter list is told by the kind, so the annotation of a member's only argument stays convertible for an anonymous record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of the dotnet#20549 compaction (feature/anonymous-record-struct-refactoring 4c7066a..5be006c) to all-fixes. The range includes the pull request's merge of the compacted tuple refactoring (dotnet#20548), so this one commit covers both pull requests: all-fixes has the generalized engine of dotnet#20549 (StructPropagation.fs, TupleConversion.fs as a kind of that engine) and no TuplePropagation.fs, and the merge kept that layout. - dotnet#20548: Compact the tuple refactoring sources - dotnet#20548: Share the cross-file refactoring test setup - dotnet#20549: Compact the shared struct conversion sources - dotnet#20549: Share the cross-file test setup of the conversion tests StructPropagation.fs reads the related uses once now (ForEachUse) and keeps the RelatedSymbolUseKind.AllInCode argument that all-fixes needs there. RefactorTestFramework.fs merges the pull request's helpers with those of the Extract suites that the earlier commits of this series put there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ctrl+. on a tuple expression, pattern or annotated tuple type converts it to the other kind and follows the value through the solution. From dotnet#20548 (release note left out: 18.vNext.md has local edits). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548: Find All References did not see the second document added to a single-file test solution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ents with their calls From dotnet#20548 and dotnet#20549. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…et#20549) up to their pull request heads Their calls to SymbolHelpers.getSymbolUses ask for RelatedSymbolUseKind.AllInCode, as the XML doc rename work on this branch requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…annotated patterns The conversion now also follows a tuple through - a function in parentheses, `(sum) (1, 2)`; - a name bound to a lambda, its parameters and its result; - a function alias and a partial application, `let alias = make`; - a named argument, `M(p = pair)`; - an annotated tuple pattern, `((a, b): int * int)`, from the pattern, the annotation or the value; - a value whose binding head is in parentheses, `let (pair: int * int) = (1, 2)`. It is no longer offered where the result cannot compile or nothing would change: - on the annotation of a member's only argument, which is its parameter list; - on the argument of a union case whose only field is declared a tuple; - on the argument of a method in parentheses, `(c.Add) (1, 2)`; - on a tuple over several lines without parentheses, which cannot change in place. `struct` is kept apart from type arguments the parenthesis follows: `f<int> struct (1, 2)`. This is b7bc303 of dotnet#20548 as dotnet#20549 merged it (3abaa70) into the engine both kinds share: `StructConversion.fs` and `StructPropagation.fs` take what went into `TupleConversion.fs` and `TuplePropagation.fs` there. A member's parameter list is told by the kind, so the annotation of a member's only argument stays convertible for an anonymous record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of the dotnet#20549 compaction (feature/anonymous-record-struct-refactoring 4c7066a..5be006c) to all-fixes. The range includes the pull request's merge of the compacted tuple refactoring (dotnet#20548), so this one commit covers both pull requests: all-fixes has the generalized engine of dotnet#20549 (StructPropagation.fs, TupleConversion.fs as a kind of that engine) and no TuplePropagation.fs, and the merge kept that layout. - dotnet#20548: Compact the tuple refactoring sources - dotnet#20548: Share the cross-file refactoring test setup - dotnet#20549: Compact the shared struct conversion sources - dotnet#20549: Share the cross-file test setup of the conversion tests StructPropagation.fs reads the related uses once now (ForEachUse) and keeps the RelatedSymbolUseKind.AllInCode argument that all-fixes needs there. RefactorTestFramework.fs merges the pull request's helpers with those of the Extract suites that the earlier commits of this series put there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ctrl+. on a tuple expression, pattern or annotated tuple type converts it to the other kind and follows the value through the solution. From dotnet#20548 (release note left out: 18.vNext.md has local edits). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548: Find All References did not see the second document added to a single-file test solution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
From dotnet#20548. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ents with their calls From dotnet#20548 and dotnet#20549. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…et#20549) up to their pull request heads Their calls to SymbolHelpers.getSymbolUses ask for RelatedSymbolUseKind.AllInCode, as the XML doc rename work on this branch requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…annotated patterns The conversion now also follows a tuple through - a function in parentheses, `(sum) (1, 2)`; - a name bound to a lambda, its parameters and its result; - a function alias and a partial application, `let alias = make`; - a named argument, `M(p = pair)`; - an annotated tuple pattern, `((a, b): int * int)`, from the pattern, the annotation or the value; - a value whose binding head is in parentheses, `let (pair: int * int) = (1, 2)`. It is no longer offered where the result cannot compile or nothing would change: - on the annotation of a member's only argument, which is its parameter list; - on the argument of a union case whose only field is declared a tuple; - on the argument of a method in parentheses, `(c.Add) (1, 2)`; - on a tuple over several lines without parentheses, which cannot change in place. `struct` is kept apart from type arguments the parenthesis follows: `f<int> struct (1, 2)`. This is b7bc303 of dotnet#20548 as dotnet#20549 merged it (3abaa70) into the engine both kinds share: `StructConversion.fs` and `StructPropagation.fs` take what went into `TupleConversion.fs` and `TuplePropagation.fs` there. A member's parameter list is told by the kind, so the annotation of a member's only argument stays convertible for an anonymous record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of the dotnet#20549 compaction (feature/anonymous-record-struct-refactoring 4c7066a..5be006c) to all-fixes. The range includes the pull request's merge of the compacted tuple refactoring (dotnet#20548), so this one commit covers both pull requests: all-fixes has the generalized engine of dotnet#20549 (StructPropagation.fs, TupleConversion.fs as a kind of that engine) and no TuplePropagation.fs, and the merge kept that layout. - dotnet#20548: Compact the tuple refactoring sources - dotnet#20548: Share the cross-file refactoring test setup - dotnet#20549: Compact the shared struct conversion sources - dotnet#20549: Share the cross-file test setup of the conversion tests StructPropagation.fs reads the related uses once now (ForEachUse) and keeps the RelatedSymbolUseKind.AllInCode argument that all-fixes needs there. RefactorTestFramework.fs merges the pull request's helpers with those of the Extract suites that the earlier commits of this series put there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Offers
Ctrl+.on a tuple expression, pattern or annotated tuple type to switch it between a reference and a struct tuple. The value is followed through annotations, patterns, record fields, function results and call arguments across the solution, so the result still type-checks. What can't be followed (fst,snd, generic collections) is left for the compiler to report.Not offered for method argument lists, the only argument of a member (that is its parameter list), inside quotations, or when the file has a paired
.fsi.🤖 Generated with Claude Code