Swift: extract iterator variable for for-in loops

This commit is contained in:
Robert Marsh
2023-09-08 19:08:57 +00:00
parent 50d23f145b
commit a3e250aef5
10 changed files with 7002 additions and 5884 deletions

View File

@@ -73,8 +73,9 @@ codeql::ForEachStmt StmtTranslator::translateForEachStmt(const swift::ForEachStm
auto entry = dispatcher.createEntry(stmt);
fillLabeledStmt(stmt, entry);
entry.body = dispatcher.fetchLabel(stmt.getBody());
entry.sequence = dispatcher.fetchLabel(stmt.getTypeCheckedSequence());
// entry.sequence = dispatcher.fetchLabel(stmt.getTypeCheckedSequence());
entry.pattern = dispatcher.fetchLabel(stmt.getPattern());
entry.iteratorVar = dispatcher.fetchLabel(stmt.getIteratorVar());
entry.where = dispatcher.fetchOptionalLabel(stmt.getWhere());
entry.nextCall = dispatcher.fetchOptionalLabel(stmt.getNextCall());
return entry;

View File

@@ -379,10 +379,8 @@ lib/codeql/swift/generated/KeyPathComponent.qll c79c7bc04fc1426992ab472eedc1a20a
lib/codeql/swift/generated/Locatable.qll be20967d48a34cdba126fe298606e0adc11697831f097acba9c52a0b7ce9983e 8aa01bc376614abbc3209e25785c72f86c9b4e94bb5f471a4a0677fedaec4f61
lib/codeql/swift/generated/Location.qll c5793987e77812059a28254dadee29bfe9b38153c0399fbb1bf6a2f5c237fdab 6e6d8802b021e36bbaad81845657769dd48a798ea33080ada05e9818a20b38f7
lib/codeql/swift/generated/OtherAvailabilitySpec.qll 0e26a203b26ff0581b7396b0c6d1606feec5cc32477f676585cdec4911af91c5 0e26a203b26ff0581b7396b0c6d1606feec5cc32477f676585cdec4911af91c5
lib/codeql/swift/generated/ParentChild.qll 04b2871246a5d9c028de9add03077ee8728a418bd03f72fc2585fedd6b3ab630 03ccfa01fad473385a3fa467e0a784419955a1437fd5e432da3877b0ac369186
lib/codeql/swift/generated/PlatformVersionAvailabilitySpec.qll f82d9ca416fe8bd59b5531b65b1c74c9f317b3297a6101544a11339a1cffce38 7f5c6d3309e66c134107afe55bae76dfc9a72cb7cdd6d4c3706b6b34cee09fa0
lib/codeql/swift/generated/PureSynthConstructors.qll 173c0dd59396a1de26fe870e3bc2766c46de689da2a4d8807cb62023bbce1a98 173c0dd59396a1de26fe870e3bc2766c46de689da2a4d8807cb62023bbce1a98
lib/codeql/swift/generated/Raw.qll a216c5a0fd2331229db65795df7c2380ac51c7bc735c43ca985560183071d646 6afeb64476f461421c9e0958240e9771a543b198d78eb880325a436fd3a51f04
lib/codeql/swift/generated/Synth.qll 551fdf7e4b53f9ee1314d1bb42c2638cf82f45bfa1f40a635dfa7b6072e4418c 9ab178464700a19951fc5285acacda4913addee81515d8e072b3d7055935a814
lib/codeql/swift/generated/SynthConstructors.qll 2f801bd8b0db829b0253cd459ed3253c1fdfc55dce68ebc53e7fec138ef0aca4 2f801bd8b0db829b0253cd459ed3253c1fdfc55dce68ebc53e7fec138ef0aca4
lib/codeql/swift/generated/UnknownFile.qll 0fcf9beb8de79440bcdfff4bb6ab3dd139bd273e6c32754e05e6a632651e85f6 0fcf9beb8de79440bcdfff4bb6ab3dd139bd273e6c32754e05e6a632651e85f6
@@ -576,7 +574,6 @@ lib/codeql/swift/generated/stmt/DoCatchStmt.qll b418fdb6d48c2c0d30d11c0b256692af
lib/codeql/swift/generated/stmt/DoStmt.qll 582f56113ecc384ee80610ae80e2a040fbe58c56b72c76b6c7da3daaeee739bd 3778445dc2f6173d4182cbda47ca0d0e066d931379ed7da89bb3afd1fda1e81b
lib/codeql/swift/generated/stmt/FailStmt.qll d8f5816c51c5027fd6dacc8d9f5ddd21f691c138dfc80c6c79e250402a1fe165 d8f5816c51c5027fd6dacc8d9f5ddd21f691c138dfc80c6c79e250402a1fe165
lib/codeql/swift/generated/stmt/FallthroughStmt.qll aa400a95593395d97b196a78462fb5ab7cad0497b395cdd98885e1593271614d 4df6bfa7d2f4e2b5e5155351e445bb6c710e7c20c82fa3321564b11ef60b086a
lib/codeql/swift/generated/stmt/ForEachStmt.qll 766bdc3ceb4271c2813d4c6dbdd784b6d1d098094aa8caa439868493bad35648 b436469bbb584b35a02a143a3132dd728822e55cc03b041ffc5bb822a863d450
lib/codeql/swift/generated/stmt/GuardStmt.qll f31660bbe32231e310ff3d33dfece761ee7ec888fe74683359f86a3766e7c378 ce1f8279839e0b6311107ea9473871cbcfdc7c12d2368ac55b989f9bff2c5e7c
lib/codeql/swift/generated/stmt/IfStmt.qll 80f1caba3a477e589b6aa3543ec1787005ab1ffab91a77832512c79dffce48c7 2126cf386e917a230175ba7e07450e390b4bd65da6fce1af8395e5ffd3f79dca
lib/codeql/swift/generated/stmt/LabeledConditionalStmt.qll 057c6c556ecd836ca7f40d208c04e43039dde53e41eb27cc27f5f502a38a86fa 2ee979a35e0e9fa72253ab21d57c18b7268b7acc1edb4ec514b73b99b0aa2c6c

3
swift/ql/.gitattributes generated vendored
View File

@@ -381,10 +381,8 @@
/lib/codeql/swift/generated/Locatable.qll linguist-generated
/lib/codeql/swift/generated/Location.qll linguist-generated
/lib/codeql/swift/generated/OtherAvailabilitySpec.qll linguist-generated
/lib/codeql/swift/generated/ParentChild.qll linguist-generated
/lib/codeql/swift/generated/PlatformVersionAvailabilitySpec.qll linguist-generated
/lib/codeql/swift/generated/PureSynthConstructors.qll linguist-generated
/lib/codeql/swift/generated/Raw.qll linguist-generated
/lib/codeql/swift/generated/Synth.qll linguist-generated
/lib/codeql/swift/generated/SynthConstructors.qll linguist-generated
/lib/codeql/swift/generated/UnknownFile.qll linguist-generated
@@ -578,7 +576,6 @@
/lib/codeql/swift/generated/stmt/DoStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/FailStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/FallthroughStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/ForEachStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/GuardStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/IfStmt.qll linguist-generated
/lib/codeql/swift/generated/stmt/LabeledConditionalStmt.qll linguist-generated

View File

@@ -486,7 +486,9 @@ module Stmts {
override ForEachStmt ast;
final override predicate propagatesAbnormal(ControlFlowElement child) {
child.asAstNode() = ast.getSequence().getFullyConverted()
child.asAstNode() = ast.getIteratorVar()
or
child.asAstNode() = ast.getNextCall()
or
child.asAstNode() = ast.getPattern().getFullyUnresolved()
}
@@ -495,7 +497,7 @@ module Stmts {
// Unlike most other statements, `foreach` statements are not modeled in
// pre-order, because we use the `foreach` node itself to represent the
// emptiness test that determines whether to execute the loop body
astFirst(ast.getSequence().getFullyConverted(), first)
astFirst(ast.getIteratorVar(), first)
}
final override predicate last(ControlFlowElement last, Completion c) {
@@ -518,8 +520,13 @@ module Stmts {
}
override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
// Flow from last element of iterator expression to emptiness test
astLast(ast.getSequence().getFullyConverted(), pred, c) and
// Flow from last element of iterator expression to first element of iterator call
astLast(ast.getIteratorVar(), pred, c) and
c instanceof NormalCompletion and
astFirst(ast.getNextCall(), succ)
or
// Flow from iterator call to emptiness test
astLast(ast.getNextCall(), pred, c) and
c instanceof NormalCompletion and
succ.asAstNode() = ast
or
@@ -555,7 +562,7 @@ module Stmts {
// Flow from last element of loop body back to emptiness test.
astLast(ast.getBody(), pred, c) and
c.continuesLoop(ast) and
succ.asAstNode() = ast
astFirst(ast.getNextCall(), succ)
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ import codeql.swift.elements.stmt.BraceStmt
import codeql.swift.elements.expr.Expr
import codeql.swift.elements.stmt.LabeledStmt
import codeql.swift.elements.pattern.Pattern
import codeql.swift.elements.decl.PatternBindingDecl
module Generated {
class ForEachStmt extends Synth::TForEachStmt, LabeledStmt {
@@ -17,44 +18,18 @@ module Generated {
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
Pattern getImmediatePattern() {
result =
Synth::convertPatternFromRaw(Synth::convertForEachStmtToRaw(this)
.(Raw::ForEachStmt)
.getPattern())
result = Synth::convertPatternFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getPattern())
}
/**
* Gets the pattern of this for each statement.
*
*/
final Pattern getPattern() {
exists(Pattern immediate |
immediate = this.getImmediatePattern() and
result = immediate.resolve()
)
exists(Pattern immediate | immediate = this.getImmediatePattern() and
result = immediate.resolve())
}
/**
* Gets the sequence of this for each statement.
*
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
Expr getImmediateSequence() {
result =
Synth::convertExprFromRaw(Synth::convertForEachStmtToRaw(this)
.(Raw::ForEachStmt)
.getSequence())
}
/**
* Gets the sequence of this for each statement.
*/
final Expr getSequence() {
exists(Expr immediate |
immediate = this.getImmediateSequence() and
result = immediate.resolve()
)
}
/**
* Gets the where of this for each statement, if it exists.
@@ -63,24 +38,39 @@ module Generated {
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
Expr getImmediateWhere() {
result =
Synth::convertExprFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getWhere())
result = Synth::convertExprFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getWhere())
}
/**
* Gets the where of this for each statement, if it exists.
*
*/
final Expr getWhere() {
exists(Expr immediate |
immediate = this.getImmediateWhere() and
result = immediate.resolve()
)
exists(Expr immediate | immediate = this.getImmediateWhere() and
result = immediate.resolve())
}
/**
* Holds if `getWhere()` exists.
*/
final predicate hasWhere() { exists(this.getWhere()) }
final predicate hasWhere() {
exists(this.getWhere())
}
/**
* Gets the iteratorvar of this for each statement, if it exists.
*
*/
PatternBindingDecl getIteratorVar() {
result = Synth::convertPatternBindingDeclFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getIteratorVar())
}
/**
* Holds if `getIteratorVar()` exists.
*/
final predicate hasIteratorVar() {
exists(this.getIteratorVar())
}
/**
* Gets the nextcall of this for each statement, if it exists.
@@ -89,35 +79,32 @@ module Generated {
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
Expr getImmediateNextCall() {
result =
Synth::convertExprFromRaw(Synth::convertForEachStmtToRaw(this)
.(Raw::ForEachStmt)
.getNextCall())
result = Synth::convertExprFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getNextCall())
}
/**
* Gets the nextcall of this for each statement, if it exists.
*
*/
final Expr getNextCall() {
exists(Expr immediate |
immediate = this.getImmediateNextCall() and
result = immediate.resolve()
)
exists(Expr immediate | immediate = this.getImmediateNextCall() and
result = immediate.resolve())
}
/**
* Holds if `getNextCall()` exists.
*/
final predicate hasNextCall() { exists(this.getNextCall()) }
final predicate hasNextCall() {
exists(this.getNextCall())
}
/**
* Gets the body of this for each statement.
*
*/
BraceStmt getBody() {
result =
Synth::convertBraceStmtFromRaw(Synth::convertForEachStmtToRaw(this)
.(Raw::ForEachStmt)
.getBody())
result = Synth::convertBraceStmtFromRaw(Synth::convertForEachStmtToRaw(this).(Raw::ForEachStmt).getBody())
}
}
}

View File

@@ -1908,7 +1908,6 @@ do_stmts( //dir=stmt
for_each_stmts( //dir=stmt
unique int id: @for_each_stmt,
int pattern: @pattern_or_none ref,
int sequence: @expr_or_none ref,
int body: @brace_stmt_or_none ref
);
@@ -1918,6 +1917,12 @@ for_each_stmt_wheres( //dir=stmt
int where: @expr_or_none ref
);
#keyset[id]
for_each_stmt_iterator_vars( //dir=stmt
int id: @for_each_stmt ref,
int iteratorVar: @pattern_binding_decl_or_none ref
);
#keyset[id]
for_each_stmt_next_calls( //dir=stmt
int id: @for_each_stmt ref,

View File

@@ -703,18 +703,20 @@ cfg.swift:
# 155| Type = Int?
# 138| getElement(0): [ForEachStmt] for ... in ... { ... }
# 138| getPattern(): [AnyPattern] _
# 138| getSequence(): [CallExpr] call to makeIterator()
# 138| getFunction(): [MethodLookupExpr] .makeIterator()
# 138| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 138| getFunction(): [MethodLookupExpr] ....(_:_:)
# 138| getBase(): [TypeExpr] Int.Type
# 138| getTypeRepr(): [TypeRepr] Int
# 138| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 138| getArgument(0): [Argument] : 0
# 138| getExpr(): [IntegerLiteralExpr] 0
# 138| getArgument(1): [Argument] : 10
# 138| getExpr(): [IntegerLiteralExpr] 10
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
#-----| getIteratorVar(): [PatternBindingDecl] var ... = ...
# 138| getInit(0): [CallExpr] call to makeIterator()
# 138| getFunction(): [MethodLookupExpr] .makeIterator()
# 138| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 138| getFunction(): [MethodLookupExpr] ....(_:_:)
# 138| getBase(): [TypeExpr] Int.Type
# 138| getTypeRepr(): [TypeRepr] Int
# 138| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 138| getArgument(0): [Argument] : 0
# 138| getExpr(): [IntegerLiteralExpr] 0
# 138| getArgument(1): [Argument] : 10
# 138| getExpr(): [IntegerLiteralExpr] 10
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
# 138| getPattern(0): [NamedPattern] $generator
# 138| getNextCall(): [CallExpr] call to next()
# 138| getFunction(): [MethodLookupExpr] .next()
# 138| getBase(): [DeclRefExpr] $generator
@@ -842,7 +844,6 @@ cfg.swift:
# 158| getElse(): [BraceStmt] { ... }
# 159| getElement(0): [ReturnStmt] return ...
# 159| getResult(): [BooleanLiteralExpr] false
# 138| [NamedPattern] $generator
# 138| [ConcreteVarDecl] $generator
# 138| Type = IndexingIterator<ClosedRange<Int>>
# 141| [ConcreteVarDecl] $match
@@ -3258,18 +3259,20 @@ cfg.swift:
# 525| getBody(): [BraceStmt] { ... }
# 526| getElement(0): [ForEachStmt] for ... in ... { ... }
# 526| getPattern(): [NamedPattern] i
# 526| getSequence(): [CallExpr] call to makeIterator()
# 526| getFunction(): [MethodLookupExpr] .makeIterator()
# 526| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 526| getFunction(): [MethodLookupExpr] ....(_:_:)
# 526| getBase(): [TypeExpr] Int.Type
# 526| getTypeRepr(): [TypeRepr] Int
# 526| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 526| getArgument(0): [Argument] : 1
# 526| getExpr(): [IntegerLiteralExpr] 1
# 526| getArgument(1): [Argument] : 100
# 526| getExpr(): [IntegerLiteralExpr] 100
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
#-----| getIteratorVar(): [PatternBindingDecl] var ... = ...
# 526| getInit(0): [CallExpr] call to makeIterator()
# 526| getFunction(): [MethodLookupExpr] .makeIterator()
# 526| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 526| getFunction(): [MethodLookupExpr] ....(_:_:)
# 526| getBase(): [TypeExpr] Int.Type
# 526| getTypeRepr(): [TypeRepr] Int
# 526| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 526| getArgument(0): [Argument] : 1
# 526| getExpr(): [IntegerLiteralExpr] 1
# 526| getArgument(1): [Argument] : 100
# 526| getExpr(): [IntegerLiteralExpr] 100
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
# 526| getPattern(0): [NamedPattern] $i$generator
# 526| getNextCall(): [CallExpr] call to next()
# 526| getFunction(): [MethodLookupExpr] .next()
# 526| getBase(): [DeclRefExpr] $i$generator
@@ -3291,11 +3294,13 @@ cfg.swift:
# 523| getPattern(0): [NamedPattern] stream
# 533| getElement(1): [ForEachStmt] for ... in ... { ... }
# 533| getPattern(): [NamedPattern] i
# 533| getSequence(): [CallExpr] call to makeAsyncIterator()
# 533| getFunction(): [MethodLookupExpr] .makeAsyncIterator()
# 533| getBase(): [DeclRefExpr] stream
# 533| getBase().getFullyConverted(): [LoadExpr] (AsyncStream<Int>) ...
#-----| getMethodRef(): [DeclRefExpr] makeAsyncIterator()
#-----| getIteratorVar(): [PatternBindingDecl] var ... = ...
# 533| getInit(0): [CallExpr] call to makeAsyncIterator()
# 533| getFunction(): [MethodLookupExpr] .makeAsyncIterator()
# 533| getBase(): [DeclRefExpr] stream
# 533| getBase().getFullyConverted(): [LoadExpr] (AsyncStream<Int>) ...
#-----| getMethodRef(): [DeclRefExpr] makeAsyncIterator()
# 533| getPattern(0): [NamedPattern] $i$generator
# 533| getNextCall(): [CallExpr] call to next()
# 533| getFunction(): [MethodLookupExpr] .next()
# 533| getBase(): [DeclRefExpr] $i$generator
@@ -3317,12 +3322,10 @@ cfg.swift:
# 525| [NilLiteralExpr] nil
# 526| [ConcreteVarDecl] i
# 526| Type = Int
# 526| [NamedPattern] $i$generator
# 526| [ConcreteVarDecl] $i$generator
# 526| Type = IndexingIterator<ClosedRange<Int>>
# 533| [ConcreteVarDecl] i
# 533| Type = Int
# 533| [NamedPattern] $i$generator
# 533| [ConcreteVarDecl] $i$generator
# 533| Type = AsyncStream<Int>.Iterator
declarations.swift:
@@ -6612,18 +6615,20 @@ statements.swift:
# 9| Type = Int
# 2| getElement(0): [ForEachStmt] for ... in ... { ... }
# 2| getPattern(): [NamedPattern] i
# 2| getSequence(): [CallExpr] call to makeIterator()
# 2| getFunction(): [MethodLookupExpr] .makeIterator()
# 2| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 2| getFunction(): [MethodLookupExpr] ....(_:_:)
# 2| getBase(): [TypeExpr] Int.Type
# 2| getTypeRepr(): [TypeRepr] Int
# 2| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 2| getArgument(0): [Argument] : 1
# 2| getExpr(): [IntegerLiteralExpr] 1
# 2| getArgument(1): [Argument] : 5
# 2| getExpr(): [IntegerLiteralExpr] 5
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
#-----| getIteratorVar(): [PatternBindingDecl] var ... = ...
# 2| getInit(0): [CallExpr] call to makeIterator()
# 2| getFunction(): [MethodLookupExpr] .makeIterator()
# 2| getBase(): [BinaryExpr] ... ....(_:_:) ...
# 2| getFunction(): [MethodLookupExpr] ....(_:_:)
# 2| getBase(): [TypeExpr] Int.Type
# 2| getTypeRepr(): [TypeRepr] Int
# 2| getMethodRef(): [DeclRefExpr] ...(_:_:)
# 2| getArgument(0): [Argument] : 1
# 2| getExpr(): [IntegerLiteralExpr] 1
# 2| getArgument(1): [Argument] : 5
# 2| getExpr(): [IntegerLiteralExpr] 5
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
# 2| getPattern(0): [NamedPattern] $i$generator
# 2| getNextCall(): [CallExpr] call to next()
# 2| getFunction(): [MethodLookupExpr] .next()
# 2| getBase(): [DeclRefExpr] $i$generator
@@ -6768,7 +6773,6 @@ statements.swift:
# 29| getPattern().getFullyUnresolved(): [BindingPattern] let ...
# 2| [ConcreteVarDecl] i
# 2| Type = Int
# 2| [NamedPattern] $i$generator
# 2| [ConcreteVarDecl] $i$generator
# 2| Type = IndexingIterator<ClosedRange<Int>>
# 21| [ConcreteVarDecl] error
@@ -7044,10 +7048,6 @@ statements.swift:
# 71| getBody(): [BraceStmt] { ... }
# 71| getElement(0): [ForEachStmt] for ... in ... where ... { ... }
# 71| getPattern(): [NamedPattern] number
# 71| getSequence(): [CallExpr] call to makeIterator()
# 71| getFunction(): [MethodLookupExpr] .makeIterator()
# 71| getBase(): [DeclRefExpr] numbers
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
# 71| getWhere(): [BinaryExpr] ... .==(_:_:) ...
# 71| getFunction(): [MethodLookupExpr] .==(_:_:)
# 71| getBase(): [TypeExpr] Int.Type
@@ -7065,6 +7065,12 @@ statements.swift:
# 71| getExpr(): [IntegerLiteralExpr] 2
# 71| getArgument(1): [Argument] : 0
# 71| getExpr(): [IntegerLiteralExpr] 0
#-----| getIteratorVar(): [PatternBindingDecl] var ... = ...
# 71| getInit(0): [CallExpr] call to makeIterator()
# 71| getFunction(): [MethodLookupExpr] .makeIterator()
# 71| getBase(): [DeclRefExpr] numbers
#-----| getMethodRef(): [DeclRefExpr] makeIterator()
# 71| getPattern(0): [NamedPattern] $number$generator
# 71| getNextCall(): [CallExpr] call to next()
# 71| getFunction(): [MethodLookupExpr] .next()
# 71| getBase(): [DeclRefExpr] $number$generator
@@ -7073,7 +7079,6 @@ statements.swift:
# 71| getBody(): [BraceStmt] { ... }
# 71| [ConcreteVarDecl] number
# 71| Type = Int
# 71| [NamedPattern] $number$generator
# 71| [ConcreteVarDecl] $number$generator
# 71| Type = IndexingIterator<[Int]>
# 74| [StructDecl] HasModifyAccessorDecl

View File

@@ -986,8 +986,8 @@ class DoStmt(LabeledStmt):
class ForEachStmt(LabeledStmt):
pattern: Pattern | child
sequence: Expr | child
where: optional[Expr] | child
iteratorVar: optional[PatternBindingDecl] | child
nextCall: optional[Expr] | child
body: BraceStmt | child