Skip to content

Commit f163e95

Browse files
committed
feat(ast): Implement conditionals and loops support in Bicep AST
1 parent 5a8abc9 commit f163e95

File tree

5 files changed

+147
-13
lines changed

5 files changed

+147
-13
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Loop statements are not supported in `bicep` code.
3+
*/
4+
5+
private import Expr
6+
private import Stmts
7+
private import internal.Conditionals
8+
9+
10+
class Conditionals extends Stmts instanceof ConditionalsImpl {
11+
/**
12+
* Gets the condition expression of this conditional statement, if any.
13+
*
14+
* For if/elseif statements, this is the boolean expression in parentheses.
15+
* For switch statements, this is the expression being switched on.
16+
* For else clauses and default cases, this returns no result as they have no condition.
17+
*/
18+
Expr getCondition() { result = ConditionalsImpl.super.getCondition() }
19+
20+
/**
21+
* Gets the branch (statement sequence) executed when this conditional is satisfied.
22+
*
23+
* This represents the code that runs when the condition is true or when
24+
* this branch is selected in a switch/match statement.
25+
*/
26+
StmtSequence getBranch() { result = ConditionalsImpl.super.getBranch() }
27+
}

ql/lib/codeql/bicep/ast/Loops.qll

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,72 @@
1+
private import AstNodes
2+
private import Expr
3+
private import Idents
4+
private import Stmts
5+
private import internal.Loops
6+
private import internal.ForStatement
7+
8+
class Loops extends Stmts instanceof LoopsImpl {
9+
/**
10+
* Gets the condition expression of the loop.
11+
*
12+
* This is the expression that determines whether the loop continues executing.
13+
* For while and for loops, this is the explicit condition expression.
14+
* For do-while loops, this is the condition evaluated after each iteration.
15+
* For foreach loops, this represents an implicit condition checking if there are more elements.
16+
*/
17+
abstract Expr getCondition();
18+
19+
/**
20+
* Gets the body of the loop.
21+
*
22+
* The body contains the statements that are executed in each iteration of the loop.
23+
* In the CFG, control typically flows back from the end of the body to the loop condition
24+
* or to the update expression (in for loops).
25+
*/
26+
abstract Stmts getBody();
27+
28+
/**
29+
* Gets the initializer of the loop, if any.
30+
*
31+
* For for loops, this includes the variables initialized before the loop starts.
32+
* For foreach loops, this includes the key and value variables declared for iteration.
33+
* Other loop types may not have initializers.
34+
*/
35+
abstract Idents getInitializer();
36+
}
137

238
/**
3-
* Loop statements are not supported in `bicep` code.
39+
* A for statement in Bicep.
40+
*
41+
* In Bicep, for loops are used to iterate over arrays, integer ranges, or object properties.
42+
* They can be used to create multiple instances of resources, modules, variables, or properties.
43+
*
44+
* Example:
45+
* ```bicep
46+
* // Creating multiple storage accounts using a for loop with range
47+
* resource storageAcct 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(0, storageCount): {
48+
* name: '${i}storage${uniqueString(resourceGroup().id)}'
49+
* location: location
50+
* sku: {
51+
* name: 'Standard_LRS'
52+
* }
53+
* kind: 'Storage'
54+
* }]
55+
* ```
456
*/
57+
class ForStatement extends Loops instanceof ForStatementImpl {
58+
/** Gets the condition expression of this for loop. */
59+
override Expr getCondition() {
60+
result = ForStatementImpl.super.getCondition()
61+
}
62+
63+
/** Gets the body of this for loop. */
64+
override Stmts getBody() {
65+
result = ForStatementImpl.super.getBody()
66+
}
67+
68+
/** Gets the initializer of this for loop. */
69+
override Idents getInitializer() {
70+
result = ForStatementImpl.super.getInitializer()
71+
}
72+
}

ql/lib/codeql/bicep/ast/internal/Conditionals.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ private import codeql.bicep.ast.AstNodes
66
private import AstNodes
77
private import TreeSitter
88
private import Expr
9+
private import Stmts
10+
// Re-exports
11+
import ForStatement
912

1013
/**
1114
* Conditional statements.
1215
*/
13-
class ConditionalExprImpl extends ExprImpl, TConditionalExpr {
16+
class ConditionalsImpl extends StmtsImpl, TConditionals {
1417
override string getAPrimaryQlClass() { result = "Conditional" }
1518

1619
abstract ExprImpl getCondition();
20+
21+
abstract StmtSequenceImpl getBranch();
1722
}
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,41 @@
11
/**
22
* Internal implementation for ForStatement
3-
*
4-
* WARNING: this file is generated, do not edit manually
53
*/
4+
65
private import AstNodes
76
private import TreeSitter
87
private import codeql.bicep.ast.AstNodes
98
private import Stmts
10-
9+
private import Loops
10+
private import Expr
11+
private import Idents
1112

1213
/**
1314
* A ForStatement AST Node.
1415
*/
15-
class ForStatementImpl extends TForStatement, StmtsImpl {
16+
class ForStatementImpl extends TForStatement, LoopsImpl {
1617
private BICEP::ForStatement ast;
1718

1819
override string getAPrimaryQlClass() { result = "ForStatement" }
1920

2021
ForStatementImpl() { this = TForStatement(ast) }
2122

2223
override string toString() { result = ast.toString() }
23-
24-
25-
26-
}
24+
25+
override ExprImpl getCondition() {
26+
// In Bicep, for loops don't have explicit conditions like other languages,
27+
// but the loop enumeration object can be considered the condition
28+
// We use child index 1 which should be the loop condition/enumeration
29+
toTreeSitter(result) = ast.getChild(1)
30+
}
31+
32+
override ExprImpl getBody() {
33+
// The body is the part to execute for each iteration
34+
toTreeSitter(result) = ast.getBody()
35+
}
36+
37+
override IdentsImpl getInitializer() {
38+
// The initializer is the loop variable
39+
toTreeSitter(result) = ast.getInitializer()
40+
}
41+
}
Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1-
21
/**
3-
* Loop statements are not supported in `bicep` code.
4-
*/
2+
* Loop statements in Bicep code.
3+
*
4+
* Bicep supports for loops for iterating over collections, ranges, or object properties.
5+
* These loops are used to create multiple instances of resources, variables, or other elements.
6+
*/
7+
8+
private import AstNodes
9+
private import TreeSitter
10+
private import codeql.bicep.ast.AstNodes
11+
private import Idents
12+
private import Stmts
13+
private import Expr
14+
15+
abstract class LoopsImpl extends AstNode, TLoops {
16+
override string getAPrimaryQlClass() { result = "Loops" }
17+
18+
abstract ExprImpl getCondition();
19+
20+
abstract ExprImpl getBody();
21+
22+
abstract IdentsImpl getInitializer();
23+
}

0 commit comments

Comments
 (0)