this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/parser: simplify else/fallback clause rules

Simplify the comprehension fallback clause rules:
- `else` is used only with single `if` or single `try` clause
- `fallback` is used for everything else (multiple clauses, for clauses, combinations)

This removes the ambiguity around which clauses require which keywords
and provides clearer semantics: `else` for binary choice, `fallback`
for all other cases.

Fixes:
- formatter and debug output now check ALL clauses for ForClause,
not just the first, ensuring correct keyword output for cases
like `if ... for ... fallback`

Updates:
- Parser validation logic simplified
- Error messages updated to reflect new rules
- Added comprehensive test cases for multi-clause combinations
(if-if-fallback, try-try-fallback, if-for-fallback)
- Updated specification and reference documentation

All tests pass.

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: I1297e93fd52ba7bc13d32baf3db58bbc00f2fd77
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1231973
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+206 -187
+11 -12
cue/format/node.go
··· 226 226 f.after(nil) 227 227 } 228 228 229 + func fallbackKeyword(n *ast.Comprehension) token.Token { 230 + if len(n.Clauses) > 1 { 231 + return token.FALLBACK 232 + } else if _, ok := n.Clauses[0].(*ast.ForClause); ok { 233 + return token.FALLBACK 234 + } 235 + return token.ELSE 236 + } 237 + 229 238 func (f *formatter) walkListElems(list []ast.Expr) { 230 239 f.before(nil) 231 240 for _, x := range list { ··· 253 262 f.expr(n.Value) 254 263 if n.Fallback != nil { 255 264 // Use FALLBACK keyword for 'for' comprehensions, ELSE for 'if'/'try' 256 - kw := token.ELSE 257 - if len(n.Clauses) > 0 { 258 - if _, ok := n.Clauses[0].(*ast.ForClause); ok { 259 - kw = token.FALLBACK 260 - } 261 - } 265 + kw := fallbackKeyword(n) 262 266 f.print(blank, n.Fallback.Fallback, kw, blank) 263 267 f.expr(n.Fallback.Body) 264 268 } ··· 506 510 f.expr(n.Value) 507 511 if n.Fallback != nil { 508 512 // Use FALLBACK keyword for 'for' comprehensions, ELSE for 'if'/'try' 509 - kw := token.ELSE 510 - if len(n.Clauses) > 0 { 511 - if _, ok := n.Clauses[0].(*ast.ForClause); ok { 512 - kw = token.FALLBACK 513 - } 514 - } 513 + kw := fallbackKeyword(n) 515 514 f.print(blank, n.Fallback.Fallback, kw, blank) 516 515 f.expr(n.Fallback.Body) 517 516 }
+26 -24
cue/parser/parser.go
··· 1237 1237 } 1238 1238 1239 1239 // parseFallbackClause parses an else or fallback clause in a comprehension. 1240 - // It determines the appropriate keyword based on whether clauses contains a ForClause: 1241 - // - Contains ForClause: expects FALLBACK, errors on ELSE 1242 - // - No ForClause: expects ELSE, errors on FALLBACK 1240 + // It determines the appropriate keyword based on the clause composition: 1241 + // - Single if or try clause: expects ELSE, errors on FALLBACK 1242 + // - Everything else: expects FALLBACK, errors on ELSE 1243 1243 func (p *parser) parseFallbackClause(clauses []ast.Clause) *ast.FallbackClause { 1244 1244 if p.trace { 1245 1245 defer un(trace(p, "FallbackClause")) ··· 1250 1250 p.errf(p.pos, "%s requires @experiment(try)", p.tok) 1251 1251 } 1252 1252 1253 - // Determine clause composition 1254 - hasForClause := false 1255 - for _, clause := range clauses { 1256 - switch clause.(type) { 1257 - case *ast.ForClause: 1258 - hasForClause = true 1253 + // Determine if this is a single if or try clause 1254 + isSingleGuard := len(clauses) == 1 1255 + if isSingleGuard { 1256 + switch clauses[0].(type) { 1257 + case *ast.IfClause, *ast.TryClause: 1258 + // Single if or try: use else 1259 + default: 1260 + isSingleGuard = false 1259 1261 } 1260 1262 } 1261 1263 1262 1264 var pos token.Pos 1263 - if hasForClause { 1264 - // Has for clause: must use fallback 1265 - if p.tok == token.ELSE { 1266 - p.errf(p.pos, "use 'fallback' with 'for' clauses") 1267 - } 1268 - pos = p.expect(token.FALLBACK) 1269 - } else if len(clauses) > 1 { 1270 - // Multiple guard clauses: disallow else (ambiguous) 1271 - if p.tok == token.ELSE { 1272 - p.errf(p.pos, "else clause only allowed with single 'if' or 'try' clause") 1265 + if isSingleGuard { 1266 + // Single if/try clause: must use else 1267 + if p.tok == token.FALLBACK { 1268 + p.errf(p.pos, "use 'else' with single 'if' or 'try' clause") 1269 + pos = p.pos 1270 + p.next() 1271 + } else { 1272 + pos = p.expect(token.ELSE) 1273 1273 } 1274 - pos = p.expect(token.ELSE) // Will fail, but consume token 1275 1274 } else { 1276 - // Single guard clause: use else 1277 - if p.tok == token.FALLBACK { 1278 - p.errf(p.pos, "use 'else' with 'if' clauses") 1275 + // Everything else: must use fallback 1276 + if p.tok == token.ELSE { 1277 + p.errf(p.pos, "use 'fallback' for comprehensions with multiple clauses or 'for' clauses") 1278 + pos = p.pos 1279 + p.next() 1280 + } else { 1281 + pos = p.expect(token.FALLBACK) 1279 1282 } 1280 - pos = p.expect(token.ELSE) 1281 1283 } 1282 1284 body := p.parseStruct() 1283 1285 return c.closeClause(p, &ast.FallbackClause{
+38 -11
cue/parser/parser_test.go
··· 700 700 a: { 701 701 if true if false { x: 1 } else { y: 2 } 702 702 }`, 703 - out: `@experiment(try), a: {if true if false {x: 1} else {y: 2}} 704 - else clause only allowed with single 'if' or 'try' clause`, 703 + out: `@experiment(try), a: {if true if false {x: 1} fallback {y: 2}} 704 + use 'fallback' for comprehensions with multiple clauses or 'for' clauses`, 705 705 }, 706 706 { 707 707 desc: "else with chained try error", ··· 710 710 a: { 711 711 try x = val? try y = val2? { result: x } else { fallback: 0 } 712 712 }`, 713 - out: `@experiment(try), a: {try x = val? try y = val2? {result: x} else {fallback: 0}} 714 - else clause only allowed with single 'if' or 'try' clause`, 713 + out: `@experiment(try), a: {try x = val? try y = val2? {result: x} fallback {fallback: 0}} 714 + use 'fallback' for comprehensions with multiple clauses or 'for' clauses`, 715 715 }, 716 716 { 717 717 desc: "else with if and let error", ··· 720 720 a: { 721 721 if true let x = 5 { y: x } else { z: 0 } 722 722 }`, 723 - out: `@experiment(try), a: {if true let x=5 {y: x} else {z: 0}} 724 - else clause only allowed with single 'if' or 'try' clause`, 723 + out: `@experiment(try), a: {if true let x=5 {y: x} fallback {z: 0}} 724 + use 'fallback' for comprehensions with multiple clauses or 'for' clauses`, 725 725 }, 726 726 { 727 727 desc: "else with if and try error", ··· 730 730 a: { 731 731 if true try { x: val? } else { y: 0 } 732 732 }`, 733 - out: `@experiment(try), a: {if true try {x: val?} else {y: 0}} 734 - else clause only allowed with single 'if' or 'try' clause`, 733 + out: `@experiment(try), a: {if true try {x: val?} fallback {y: 0}} 734 + use 'fallback' for comprehensions with multiple clauses or 'for' clauses`, 735 735 }, 736 736 { 737 737 desc: "else with for clause error", ··· 740 740 a: { 741 741 if true for x in [] { y: x } else { empty: true } 742 742 }`, 743 - out: `@experiment(try), a: {if true for x in [] {y: x} else {empty: true}} 744 - expected 'fallback', found 'else'`, 743 + out: `@experiment(try), a: {if true for x in [] {y: x} fallback {empty: true}} 744 + use 'fallback' for comprehensions with multiple clauses or 'for' clauses`, 745 745 }, 746 746 { 747 747 desc: "fallback without for clause error", ··· 751 751 if true { x: 1 } fallback { y: 2 } 752 752 }`, 753 753 out: `@experiment(try), a: {if true {x: 1} else {y: 2}} 754 - expected 'else', found 'fallback'`, 754 + use 'else' with single 'if' or 'try' clause`, 755 755 }, 756 756 { 757 757 desc: "single if with else allowed", ··· 788 788 for x in list if x > 0 { y: x } fallback { empty: true } 789 789 }`, 790 790 out: `@experiment(try), a: {for x in list if x>0 {y: x} fallback {empty: true}}`, 791 + }, 792 + { 793 + desc: "if with for and fallback allowed", 794 + version: "v0.16.0", 795 + in: `@experiment(try) 796 + a: { 797 + if true for x in list { y: x } fallback { empty: true } 798 + }`, 799 + out: `@experiment(try), a: {if true for x in list {y: x} fallback {empty: true}}`, 800 + }, 801 + { 802 + desc: "two if clauses with fallback allowed", 803 + version: "v0.16.0", 804 + in: `@experiment(try) 805 + a: { 806 + if cond1 if cond2 { a: 1 } fallback { b: 2 } 807 + }`, 808 + out: `@experiment(try), a: {if cond1 if cond2 {a: 1} fallback {b: 2}}`, 809 + }, 810 + { 811 + desc: "two try clauses with fallback allowed", 812 + version: "v0.16.0", 813 + in: `@experiment(try) 814 + a: { 815 + try x = a? try y = b? { result: x + y } fallback { default: 0 } 816 + }`, 817 + out: `@experiment(try), a: {try x = a? try y = b? {result: x+y} fallback {default: 0}}`, 791 818 }, 792 819 { 793 820 desc: "let declaration",
+88 -97
cue/testdata/comprehensions/else.txtar
··· 46 46 if true for x in [1, 2] { "\(x)": x } fallback { empty: true } 47 47 } 48 48 49 + // if-if-fallback: both true - no fallback 50 + ifIfBothTrue: { 51 + if true if true { a: 1 } fallback { b: 2 } 52 + } 53 + 54 + // if-if-fallback: first false - fallback yields 55 + ifIfFirstFalse: { 56 + if false if true { a: 1 } fallback { b: 2 } 57 + } 58 + 59 + // if-if-fallback: second false - fallback yields 60 + ifIfSecondFalse: { 61 + if true if false { a: 1 } fallback { b: 2 } 62 + } 63 + 64 + // try-try-fallback: both succeed - no fallback 65 + tryTryBothSucceed: { 66 + a: 1 67 + b: 2 68 + try x = a? try y = b? { result: x + y } fallback { default: 0 } 69 + } 70 + 71 + // try-try-fallback: first fails - fallback yields 72 + tryTryFirstFails: { 73 + b: 2 74 + try x = missing? try y = b? { result: x + y } fallback { default: -1 } 75 + } 76 + 77 + // try-try-fallback: second fails - fallback yields 78 + tryTrySecondFails: { 79 + a: 1 80 + try x = a? try y = missing? { result: x + y } fallback { default: -2 } 81 + } 82 + 49 83 // list if-else: true 50 84 listIfTrue: [ 51 85 if true { 1 } else { 2 } ··· 135 169 136 170 NumCloseIDs: 2 137 171 -- out/evalalpha -- 138 - (struct){ 139 - ifTrue: (struct){ 140 - a: (int){ 1 } 141 - } 142 - ifFalse: (struct){ 143 - b: (int){ 2 } 144 - } 145 - forNonEmpty: (struct){ 146 - "1": (int){ 1 } 147 - "2": (int){ 2 } 148 - } 149 - forEmpty: (struct){ 150 - empty: (bool){ true } 151 - } 152 - forFilteredAll: (struct){ 153 - empty: (bool){ true } 154 - } 155 - forFilteredSome: (struct){ 156 - "2": (int){ 2 } 157 - "3": (int){ 3 } 158 - } 159 - ifTrueForEmpty: (struct){ 160 - empty: (bool){ true } 161 - } 162 - ifFalseForNonEmpty: (struct){ 163 - empty: (bool){ true } 164 - } 165 - ifTrueForNonEmpty: (struct){ 166 - "1": (int){ 1 } 167 - "2": (int){ 2 } 168 - } 169 - listIfTrue: (#list){ 170 - 0: (int){ 1 } 171 - } 172 - listIfFalse: (#list){ 173 - 0: (int){ 2 } 174 - } 175 - listForEmpty: (#list){ 176 - 0: (int){ 0 } 177 - } 178 - listForNonEmpty: (#list){ 179 - 0: (int){ 2 } 180 - 1: (int){ 4 } 181 - } 182 - elseMultipleFields: (struct){ 183 - b: (int){ 2 } 184 - c: (int){ 3 } 185 - } 186 - elseEmbeds: (struct){ 187 - existing: (int){ 1 } 188 - fallback: (int){ 3 } 189 - } 190 - outerScope: (struct){ 191 - let threshold#1 = (int){ 10 } 192 - fallbackField: (int){ 10 } 193 - } 194 - ifNestedBody: (struct){ 195 - c: (int){ 2 } 196 - } 197 - ifNestedElse: (struct){ 198 - b: (struct){ 199 - c: (int){ 2 } 200 - } 201 - } 202 - ifNestedBoth: (struct){ 203 - c: (struct){ 204 - d: (int){ 2 } 205 - } 206 - } 207 - ifDeeplyNested: (struct){ 208 - a: (struct){ 209 - b: (struct){ 210 - c: (int){ 2 } 211 - } 212 - } 213 - } 214 - forNestedEmpty: (struct){ 215 - a: (struct){ 216 - b: (struct){ 217 - none: (bool){ true } 218 - } 219 - } 220 - } 221 - forNestedNonEmpty: (struct){ 222 - a: (struct){ 223 - b: (struct){ 224 - "1": (int){ 1 } 225 - } 226 - } 227 - } 228 - nestedOuterFallback: (struct){ 229 - outer: (bool){ true } 230 - } 231 - nestedInnerFallback: (struct){ 232 - inner: (bool){ true } 233 - } 234 - } 172 + tryTryFirstFails: reference "missing" not found: 173 + ./in.cue:73:10 174 + tryTrySecondFails.try[]: reference "missing" not found: 175 + ./in.cue:79:21 235 176 -- out/compile -- 177 + tryTryFirstFails: reference "missing" not found: 178 + ./in.cue:73:10 179 + tryTrySecondFails.try[]: reference "missing" not found: 180 + ./in.cue:79:21 236 181 --- in.cue 237 182 { 238 183 ifTrue: { ··· 313 258 "\(〈1;x〉)": 〈1;x〉 314 259 } else { 315 260 empty: true 261 + } 262 + } 263 + ifIfBothTrue: { 264 + if true if true { 265 + a: 1 266 + } else { 267 + b: 2 268 + } 269 + } 270 + ifIfFirstFalse: { 271 + if false if true { 272 + a: 1 273 + } else { 274 + b: 2 275 + } 276 + } 277 + ifIfSecondFalse: { 278 + if true if false { 279 + a: 1 280 + } else { 281 + b: 2 282 + } 283 + } 284 + tryTryBothSucceed: { 285 + a: 1 286 + b: 2 287 + try x = 〈0;a〉? try y = 〈1;b〉? { 288 + result: (〈2;x〉 + 〈1;y〉) 289 + } else { 290 + default: 0 291 + } 292 + } 293 + tryTryFirstFails: { 294 + b: 2 295 + try x = _|_(reference "missing" not found) try y = 〈1;b〉? { 296 + result: (〈2;x〉 + 〈1;y〉) 297 + } else { 298 + default: -1 299 + } 300 + } 301 + tryTrySecondFails: { 302 + a: 1 303 + try x = 〈0;a〉? try y = _|_(reference "missing" not found) { 304 + result: (〈2;x〉 + 〈1;y〉) 305 + } else { 306 + default: -2 316 307 } 317 308 } 318 309 listIfTrue: [
+33 -21
doc/specs/comprehension-else/spec.md
··· 43 43 44 44 ### Requirement: Keyword-clause validation 45 45 46 - The parser SHALL validate that the correct keyword is used based on the preceding clause type: 47 - - `else` is valid only after `if` or `try` clauses 48 - - `fallback` is valid only after `for` clauses 46 + The parser SHALL validate that the correct keyword is used based on the clause composition: 47 + - `else` is used with a single `if` or single `try` clause 48 + - `fallback` is used with everything else (multiple clauses, `for` clauses, or combinations) 49 49 50 - #### Scenario: else after if accepted 50 + #### Scenario: else with single if accepted 51 51 - **WHEN** the input is `if enabled { a: 1 } else { b: 2 }` 52 52 - **THEN** the parser SHALL accept this as valid 53 53 54 - #### Scenario: fallback after if rejected 54 + #### Scenario: else with single try accepted 55 + - **WHEN** the input is `try { a: x? } else { b: 2 }` 56 + - **THEN** the parser SHALL accept this as valid 57 + 58 + #### Scenario: fallback with single if rejected 55 59 - **WHEN** the input is `if enabled { a: 1 } fallback { b: 2 }` 56 - - **THEN** the parser SHALL report an error: "use 'else' with 'if' clauses" 60 + - **THEN** the parser SHALL report an error: "use 'else' with single 'if' or 'try' clause" 61 + 62 + #### Scenario: fallback with single try rejected 63 + - **WHEN** the input is `try { a: x? } fallback { b: 2 }` 64 + - **THEN** the parser SHALL report an error: "use 'else' with single 'if' or 'try' clause" 57 65 58 - #### Scenario: fallback after for accepted 66 + #### Scenario: fallback with for accepted 59 67 - **WHEN** the input is `for x in list { (x): true } fallback { empty: true }` 60 68 - **THEN** the parser SHALL accept this as valid 61 69 62 - #### Scenario: else after for rejected 70 + #### Scenario: else with for rejected 63 71 - **WHEN** the input is `for x in list { (x): true } else { empty: true }` 64 - - **THEN** the parser SHALL report an error: "use 'fallback' with 'for' clauses" 65 - 66 - #### Scenario: fallback after try rejected 67 - - **WHEN** the input is `try { a: x? } fallback { b: 2 }` 68 - - **THEN** the parser SHALL report an error: "use 'else' with 'try' clauses" 72 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 69 73 70 74 ### Requirement: Else and fallback clause restrictions 71 75 72 76 To avoid ambiguous semantics, the system SHALL enforce the following restrictions: 73 77 - `else` is only allowed with a single `if` clause or single `try` clause 74 - - `fallback` requires at least one `for` clause to be present in the comprehension 78 + - `fallback` is used for all other cases (multiple clauses, `for` clauses, or combinations) 75 79 76 - **Rationale**: With multiple clauses, it becomes ambiguous which clause the else applies to. Restricting `else` to a single guard clause and requiring `for` for `fallback` keeps the semantics clear and predictable. 80 + **Rationale**: The `else` keyword implies a binary choice, which only makes sense with a single conditional clause. For all other cases, `fallback` provides clear semantics: execute when the comprehension yields zero values. 77 81 78 82 #### Scenario: else with single if clause accepted 79 83 - **WHEN** the input is `if enabled { a: 1 } else { b: 2 }` ··· 85 89 86 90 #### Scenario: else with two if clauses rejected 87 91 - **WHEN** the input is `if cond1 if cond2 { a: 1 } else { b: 2 }` 88 - - **THEN** the parser SHALL report an error: "else clause only allowed with single 'if' or 'try' clause" 92 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 89 93 90 94 #### Scenario: else with chained try clauses rejected 91 95 - **WHEN** the input is `try x = a? try y = b? { result: x + y } else { fallback: 0 }` 92 - - **THEN** the parser SHALL report an error: "else clause only allowed with single 'if' or 'try' clause" 96 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 93 97 94 98 #### Scenario: else with mixed if and try rejected 95 99 - **WHEN** the input is `try x = a? if x > 0 { result: x } else { zero: true }` 96 - - **THEN** the parser SHALL report an error: "else clause only allowed with single 'if' or 'try' clause" 100 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 97 101 98 102 #### Scenario: else with if followed by for rejected 99 103 - **WHEN** the input is `if true for x in {} { x } else { a: 1 }` 100 - - **THEN** the parser SHALL report an error indicating else not allowed with for clauses 104 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 101 105 102 106 #### Scenario: else with try followed by for rejected 103 107 - **WHEN** the input is `try x = a? for y in [x] { y } else { empty: true }` 104 - - **THEN** the parser SHALL report an error indicating else not allowed with for clauses; use fallback instead 108 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 105 109 106 110 #### Scenario: else with if and let rejected 107 111 - **WHEN** the input is `if enabled let x = value { a: x } else { b: 2 }` 108 - - **THEN** the parser SHALL report an error: "else clause not allowed with let clauses" 112 + - **THEN** the parser SHALL report an error: "use 'fallback' for comprehensions with multiple clauses or 'for' clauses" 109 113 110 114 #### Scenario: fallback with for clause accepted 111 115 - **WHEN** the input is `for x in list { x } fallback { empty: true }` ··· 117 121 118 122 #### Scenario: fallback with for and let clauses accepted 119 123 - **WHEN** the input is `for x in list let y = x * 2 { y } fallback { empty: true }` 124 + - **THEN** the parser SHALL accept this as valid 125 + 126 + #### Scenario: fallback with two if clauses accepted 127 + - **WHEN** the input is `if cond1 if cond2 { a: 1 } fallback { b: 2 }` 128 + - **THEN** the parser SHALL accept this as valid 129 + 130 + #### Scenario: fallback with two try clauses accepted 131 + - **WHEN** the input is `try x = a? try y = b? { result: x + y } fallback { default: 0 }` 120 132 - **THEN** the parser SHALL accept this as valid 121 133 122 134 ### Requirement: Single else or fallback clause limit
+4 -4
internal/astinternal/debug.go
··· 424 424 if v.Fallback != nil { 425 425 // Use "fallback" for 'for' comprehensions, "else" for 'if'/'try' 426 426 kw := "else" 427 - if len(v.Clauses) > 0 { 428 - if _, ok := v.Clauses[0].(*ast.ForClause); ok { 429 - kw = "fallback" 430 - } 427 + if len(v.Clauses) > 1 { 428 + kw = "fallback" 429 + } else if _, ok := v.Clauses[0].(*ast.ForClause); ok { 430 + kw = "fallback" 431 431 } 432 432 out += " " + kw + " " + DebugStr(v.Fallback.Body) 433 433 }
+6 -18
internal/core/compile/compile.go
··· 1042 1042 // Compile fallback clause in the outer scope (before comprehension variables). 1043 1043 // The fallback clause should NOT have access to for/let variables. 1044 1044 if x.Fallback != nil { 1045 - // Validate else/fallback restrictions for programmatically created ASTs. 1046 - // The parser enforces these rules, but users can bypass the parser by 1047 - // creating AST nodes programmatically, so we validate here as well. 1048 - hasFor := false 1049 - for _, clause := range x.Clauses { 1050 - if _, ok := clause.(*ast.ForClause); ok { 1051 - hasFor = true 1052 - break 1053 - } 1054 - } 1055 1045 // Both 'else' (with if) and 'fallback' (with for) require the try experiment. 1056 1046 if !c.experiments.Try { 1057 1047 // Use appropriate keyword in error message based on clause type. 1058 - keyword := "else" 1059 - if hasFor { 1060 - keyword = "fallback" 1048 + keyword := "fallback" 1049 + if len(x.Clauses) == 1 { 1050 + switch x.Clauses[0].(type) { 1051 + case *ast.IfClause, *ast.TryClause: 1052 + keyword = "else" 1053 + } 1061 1054 } 1062 1055 return c.errf(x.Fallback, "%s clause requires the try experiment (language version v0.16.0 or later)", keyword) 1063 - } 1064 - if !hasFor && len(x.Clauses) > 1 { 1065 - // Can only trigger for programmatically created ASTs, as the parser 1066 - // enforces this rule. 1067 - return c.errf(x.Fallback, "invalid use of else or fallback") 1068 1056 } 1069 1057 1070 1058 // Pop all comprehension scopes temporarily to compile fallback in outer scope.