11private import rust
22private import codeql.rust.controlflow.ControlFlowGraph
3- private import codeql.rust.elements.internal.generated.ParentChild
3+ private import codeql.rust.elements.internal.generated.ParentChild as ParentChild
44private import codeql.rust.elements.internal.PathImpl:: Impl as PathImpl
55private import codeql.rust.elements.internal.PathExprBaseImpl:: Impl as PathExprBaseImpl
66private import codeql.rust.elements.internal.FormatTemplateVariableAccessImpl:: Impl as FormatTemplateVariableAccessImpl
@@ -36,6 +36,38 @@ module Impl {
3636 ClosureBodyScope ( ) { this = any ( ClosureExpr ce ) .getBody ( ) }
3737 }
3838
39+ /**
40+ * A scope for conditions, which may introduce variables using `let` expressions.
41+ *
42+ * Such variables are only available in the body guarded by the condition.
43+ */
44+ class ConditionScope extends VariableScope , Expr {
45+ private AstNode parent ;
46+ private AstNode body ;
47+
48+ ConditionScope ( ) {
49+ parent =
50+ any ( IfExpr ie |
51+ this = ie .getCondition ( ) and
52+ body = ie .getThen ( )
53+ )
54+ or
55+ parent =
56+ any ( WhileExpr we |
57+ this = we .getCondition ( ) and
58+ body = we .getLoopBody ( )
59+ )
60+ }
61+
62+ /** Gets the parent of this condition. */
63+ AstNode getParent ( ) { result = parent }
64+
65+ /**
66+ * Gets the body in which variables introduced in this scope are available.
67+ */
68+ AstNode getBody ( ) { result = body }
69+ }
70+
3971 private Pat getAPatAncestor ( Pat p ) {
4072 ( p instanceof IdentPat or p instanceof OrPat ) and
4173 exists ( Pat p0 | result = p0 .getParentPat ( ) |
@@ -152,8 +184,14 @@ module Impl {
152184 /** Gets the `let` statement that introduces this variable, if any. */
153185 LetStmt getLetStmt ( ) { this .getPat ( ) = result .getPat ( ) }
154186
187+ /** Gets the `let` expression that introduces this variable, if any. */
188+ LetExpr getLetExpr ( ) { this .getPat ( ) = result .getPat ( ) }
189+
155190 /** Gets the initial value of this variable, if any. */
156- Expr getInitializer ( ) { result = this .getLetStmt ( ) .getInitializer ( ) }
191+ Expr getInitializer ( ) {
192+ result = this .getLetStmt ( ) .getInitializer ( ) or
193+ result = this .getLetExpr ( ) .getScrutinee ( )
194+ }
157195
158196 /** Holds if this variable is captured. */
159197 predicate isCaptured ( ) { this .getAnAccess ( ) .isCapture ( ) }
@@ -193,15 +231,53 @@ module Impl {
193231 string getName ( ) { result = name_ }
194232 }
195233
234+ pragma [ nomagic]
235+ private Element getImmediateChildAdj ( Element e , int preOrd , int index ) {
236+ result = ParentChild:: getImmediateChild ( e , index ) and
237+ preOrd = 0 and
238+ not exists ( ConditionScope cs |
239+ e = cs .getParent ( ) and
240+ result = cs .getBody ( )
241+ )
242+ or
243+ result = e .( ConditionScope ) .getBody ( ) and
244+ preOrd = 1 and
245+ index = 0
246+ }
247+
248+ /**
249+ * An adjusted version of `ParentChild::getImmediateChild`, which makes the following
250+ * two adjustments:
251+ *
252+ * 1. For conditions like `if cond body`, instead of letting `body` be the second child
253+ * of `if`, we make it the last child of `cond`. This ensures that variables
254+ * introduced in the `cond` scope are available in `body`.
255+ *
256+ * 2. A similar adjustment is made for `while` loops: the body of the loop is made a
257+ * child of the loop condition instead of the loop itself.
258+ */
259+ pragma [ nomagic]
260+ private Element getImmediateChildAdj ( Element e , int index ) {
261+ result =
262+ rank [ index + 1 ] ( Element res , int preOrd , int i |
263+ res = getImmediateChildAdj ( e , preOrd , i )
264+ |
265+ res order by preOrd , i
266+ )
267+ }
268+
269+ private Element getImmediateParentAdj ( Element e ) { e = getImmediateChildAdj ( result , _) }
270+
196271 private AstNode getAnAncestorInVariableScope ( AstNode n ) {
197272 (
198273 n instanceof Pat or
199274 n instanceof VariableAccessCand or
200275 n instanceof LetStmt or
276+ n = any ( LetExpr le ) .getScrutinee ( ) or
201277 n instanceof VariableScope
202278 ) and
203279 exists ( AstNode n0 |
204- result = getImmediateParent ( n0 ) or
280+ result = getImmediateParentAdj ( n0 ) or
205281 result = n0 .( FormatTemplateVariableAccess ) .getArgument ( ) .getParent ( ) .getParent ( )
206282 |
207283 n0 = n
@@ -243,31 +319,32 @@ module Impl {
243319 this instanceof VariableScope or
244320 this instanceof VariableAccessCand or
245321 this instanceof LetStmt or
246- getImmediateChild ( this , _) instanceof RelevantElement
322+ this = any ( LetExpr le ) .getScrutinee ( ) or
323+ getImmediateChildAdj ( this , _) instanceof RelevantElement
247324 }
248325
249326 pragma [ nomagic]
250- private RelevantElement getChild ( int index ) { result = getImmediateChild ( this , index ) }
327+ private RelevantElement getChild ( int index ) { result = getImmediateChildAdj ( this , index ) }
251328
252329 pragma [ nomagic]
253- private RelevantElement getImmediateChildMin ( int index ) {
330+ private RelevantElement getImmediateChildAdjMin ( int index ) {
254331 // A child may have multiple positions for different accessors,
255332 // so always use the first
256333 result = this .getChild ( index ) and
257334 index = min ( int i | result = this .getChild ( i ) | i )
258335 }
259336
260337 pragma [ nomagic]
261- RelevantElement getImmediateChild ( int index ) {
338+ RelevantElement getImmediateChildAdj ( int index ) {
262339 result =
263- rank [ index + 1 ] ( Element res , int i | res = this .getImmediateChildMin ( i ) | res order by i )
340+ rank [ index + 1 ] ( Element res , int i | res = this .getImmediateChildAdjMin ( i ) | res order by i )
264341 }
265342
266343 pragma [ nomagic]
267344 RelevantElement getImmediateLastChild ( ) {
268345 exists ( int last |
269- result = this .getImmediateChild ( last ) and
270- not exists ( this .getImmediateChild ( last + 1 ) )
346+ result = this .getImmediateChildAdj ( last ) and
347+ not exists ( this .getImmediateChildAdj ( last + 1 ) )
271348 )
272349 }
273350 }
@@ -288,13 +365,13 @@ module Impl {
288365 |
289366 // first child of a previously numbered node
290367 result = getPreOrderNumbering ( scope , parent ) + 1 and
291- n = parent .getImmediateChild ( 0 )
368+ n = parent .getImmediateChildAdj ( 0 )
292369 or
293370 // non-first child of a previously numbered node
294371 exists ( RelevantElement child , int i |
295372 result = getLastPreOrderNumbering ( scope , child ) + 1 and
296- child = parent .getImmediateChild ( i ) and
297- n = parent .getImmediateChild ( i + 1 )
373+ child = parent .getImmediateChildAdj ( i ) and
374+ n = parent .getImmediateChildAdj ( i + 1 )
298375 )
299376 )
300377 }
@@ -309,7 +386,7 @@ module Impl {
309386 result = getPreOrderNumbering ( scope , leaf ) and
310387 leaf != scope and
311388 (
312- not exists ( leaf .getImmediateChild ( _) )
389+ not exists ( leaf .getImmediateChildAdj ( _) )
313390 or
314391 leaf instanceof VariableScope
315392 )
@@ -331,7 +408,7 @@ module Impl {
331408 /**
332409 * Holds if `v` is named `name` and is declared inside variable scope
333410 * `scope`. The pre-order numbering of the binding site of `v`, amongst
334- * all nodes nester under `scope`, is `ord`.
411+ * all nodes nested under `scope`, is `ord`.
335412 */
336413 private predicate variableDeclInScope ( Variable v , VariableScope scope , string name , int ord ) {
337414 name = v .getText ( ) and
@@ -354,25 +431,20 @@ module Impl {
354431 ord = getLastPreOrderNumbering ( scope , let ) + 1
355432 )
356433 or
357- exists ( IfExpr ie , LetExpr let |
434+ exists ( LetExpr let , Expr scrutinee |
358435 let .getPat ( ) = pat and
359- ie .getCondition ( ) = let and
360- scope = ie .getThen ( ) and
361- ord = getPreOrderNumbering ( scope , scope )
436+ scrutinee = let .getScrutinee ( ) and
437+ scope = getEnclosingScope ( scrutinee ) and
438+ // for `let` expressions, variables are bound _after_ the expression, i.e.
439+ // not in the RHS
440+ ord = getLastPreOrderNumbering ( scope , scrutinee ) + 1
362441 )
363442 or
364443 exists ( ForExpr fe |
365444 fe .getPat ( ) = pat and
366445 scope = fe .getLoopBody ( ) and
367446 ord = getPreOrderNumbering ( scope , scope )
368447 )
369- or
370- exists ( WhileExpr we , LetExpr let |
371- let .getPat ( ) = pat and
372- we .getCondition ( ) = let and
373- scope = we .getLoopBody ( ) and
374- ord = getPreOrderNumbering ( scope , scope )
375- )
376448 )
377449 )
378450 }
@@ -612,7 +684,7 @@ module Impl {
612684 or
613685 exists ( Expr mid |
614686 assignmentExprDescendant ( mid ) and
615- getImmediateParent ( e ) = mid and
687+ getImmediateParentAdj ( e ) = mid and
616688 not mid instanceof DerefExpr and
617689 not mid instanceof FieldExpr and
618690 not mid instanceof IndexExpr
0 commit comments