|
30 | 30 | * Mark `Range.Zero` as obsolete in favor of `Range.range0` ([PR #18664](https://github.com/dotnet/fsharp/pull/18664))
|
31 | 31 | * Use `Synbinding` to model `and!` ([PR #18805](https://github.com/dotnet/fsharp/pull/18805))
|
32 | 32 | * Redesign #line processing. The original positions (unaffected by #line directives) are now kept in the AST, and `__LINE__` and `__SOURCE_LINE__` show the original line numbers / file names. However, all diagnostics and debug information stays the same (shows the position transformed by the #line directives). ([Issue #18553](https://github.com/dotnet/fsharp/issues/18553), [PR #18699](https://github.com/dotnet/fsharp/pull/18699))
|
| 33 | +* Unify `let`, `let!`, `use` and `use!` AST representation. ([PR #18825](https://github.com/dotnet/fsharp/pull/18825))[^1] |
| 34 | + |
| 35 | +### Migration Guidance for AST Users |
| 36 | + |
| 37 | +**Note:** The unified AST introduces two new boolean fields: |
| 38 | +- `isFromSource`: Indicates if the binding comes from user-written code (`true`) or is compiler-generated (`false`) |
| 39 | +- `isBang`: Distinguishes computation expression bindings (`let!`/`use!` = `true`) from regular bindings (`let`/`use` = `false`) |
| 40 | + |
| 41 | +### 1. Pattern Matching Updates |
| 42 | + |
| 43 | +**Before:** |
| 44 | +```fsharp |
| 45 | +match expr with |
| 46 | +| SynExpr.LetOrUse(isRec, isUse, bindings, body, range, trivia) -> |
| 47 | + // Handle regular let/use |
| 48 | +| SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhs, andBangs, body, range, trivia) -> |
| 49 | + // Handle let!/use! |
| 50 | +``` |
| 51 | + |
| 52 | +**After:** |
| 53 | +```fsharp |
| 54 | +match expr with |
| 55 | +| SynExpr.LetOrUse(isRec, isUse, isFromSource, isBang, bindings, body, range, trivia) -> |
| 56 | + if isBang then |
| 57 | + // This is a let!/use! expression |
| 58 | + match bindings with |
| 59 | + | firstBinding :: andBangs -> |
| 60 | + match firstBinding with |
| 61 | + | SynBinding(headPat = pat; expr = rhs) -> |
| 62 | + // pat and rhs extracted from first binding |
| 63 | + // andBangs contains the and! bindings |
| 64 | + | [] -> // error case |
| 65 | + else |
| 66 | + // This is a regular let/use expression |
| 67 | +``` |
| 68 | + |
| 69 | +### 2. Construction Updates |
| 70 | + |
| 71 | +**Before:** |
| 72 | +```fsharp |
| 73 | +// Creating a let! expression |
| 74 | +SynExpr.LetOrUseBang( |
| 75 | + bindDebugPoint, |
| 76 | + false, // isUse |
| 77 | + true, // isFromSource |
| 78 | + pat, |
| 79 | + rhsExpr, |
| 80 | + andBangs, |
| 81 | + bodyExpr, |
| 82 | + range, |
| 83 | + trivia |
| 84 | +) |
| 85 | +``` |
| 86 | + |
| 87 | +**After:** |
| 88 | +```fsharp |
| 89 | +// Creating a let! expression |
| 90 | +let firstBinding = SynBinding( |
| 91 | + accessibility = None, |
| 92 | + kind = SynBindingKind.Normal, |
| 93 | + isInline = false, |
| 94 | + isMutable = false, |
| 95 | + attributes = [], |
| 96 | + xmlDoc = PreXmlDoc.Empty, |
| 97 | + valData = SynInfo.emptySynValData, |
| 98 | + headPat = pat, // Pattern moved here |
| 99 | + returnInfo = None, |
| 100 | + expr = rhsExpr, // RHS moved here |
| 101 | + range = range, |
| 102 | + debugPoint = bindDebugPoint, // Debug point moved here |
| 103 | + trivia = bindingTrivia |
| 104 | +) |
| 105 | +SynExpr.LetOrUse( |
| 106 | + false, // isRecursive |
| 107 | + false, // isUse |
| 108 | + true, // isFromSource |
| 109 | + true, // isBang (indicates let!) |
| 110 | + firstBinding :: andBangs, // All bindings in single list |
| 111 | + bodyExpr, |
| 112 | + range, |
| 113 | + trivia |
| 114 | +) |
| 115 | +``` |
| 116 | + |
| 117 | +### 3. Common Migration Patterns |
| 118 | + |
| 119 | +**Checking for computation expressions:** |
| 120 | +```fsharp |
| 121 | +// Before |
| 122 | +match expr with |
| 123 | +| SynExpr.LetOrUseBang _ -> true |
| 124 | +| _ -> false |
| 125 | +
|
| 126 | +// After |
| 127 | +match expr with |
| 128 | +| SynExpr.LetOrUse(isBang = true) -> true |
| 129 | +| _ -> false |
| 130 | +``` |
| 131 | + |
| 132 | +**Extracting pattern and expression from let!:** |
| 133 | +```fsharp |
| 134 | +// Before |
| 135 | +| SynExpr.LetOrUseBang(_, _, _, pat, rhs, _, _, _, _) -> |
| 136 | + processBinding pat rhs |
| 137 | +
|
| 138 | +// After |
| 139 | +| SynExpr.LetOrUse(isBang = true; bindings = binding :: _) -> |
| 140 | + match binding with |
| 141 | + | SynBinding(headPat = pat; expr = rhs) -> |
| 142 | + processBinding pat rhs |
| 143 | + | _ -> // error |
| 144 | +``` |
| 145 | + |
| 146 | +**Processing and! bindings:** |
| 147 | +```fsharp |
| 148 | +// Before |
| 149 | +| SynExpr.LetOrUseBang(_, _, _, firstPat, firstRhs, andBangs, _, _, _) -> |
| 150 | + processFirst firstPat firstRhs |
| 151 | + for andBang in andBangs do |
| 152 | + processAndBang andBang |
| 153 | +
|
| 154 | +// After |
| 155 | +| SynExpr.LetOrUse(isBang = true; bindings = bindings) -> |
| 156 | + match bindings with |
| 157 | + | first :: rest -> |
| 158 | + processBinding first |
| 159 | + for andBang in rest do |
| 160 | + processAndBang andBang |
| 161 | + | [] -> // error |
| 162 | +``` |
| 163 | + |
| 164 | +[^1]: See [Migration Guidance for AST Users](#migration-guidance-for-ast-users) section for detailed information on how to update your code to work with the unified AST representation. |
0 commit comments