diff --git a/ruby/ql/lib/change-notes/2026-06-19-rescue-exception-list.md b/ruby/ql/lib/change-notes/2026-06-19-rescue-exception-list.md new file mode 100644 index 00000000000..cc7770131ac --- /dev/null +++ b/ruby/ql/lib/change-notes/2026-06-19-rescue-exception-list.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* When a `rescue` clause has two or more exception types, the exceptions are no longer direct children of the `RescueClause` node. Instead, a new `ExceptionList` AST node wraps the exceptions. Use `RescueClause.getExceptions()` to get the `ExceptionList` node, and `ExceptionList.getException(int n)` to access the individual exceptions. For `rescue` clauses with zero or one exception, the behavior is unchanged and `RescueClause.getException(int n)` continues to work as before. diff --git a/ruby/ql/lib/codeql/ruby/ast/Expr.qll b/ruby/ql/lib/codeql/ruby/ast/Expr.qll index a49cafa8299..e45c6310f29 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Expr.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Expr.qll @@ -280,6 +280,37 @@ class Pair extends Expr instanceof PairImpl { final override string getAPrimaryQlClass() { result = "Pair" } } +/** + * A list of exception types in a rescue clause. For example, the exception list + * `FirstError, SecondError` in: + * ```rb + * begin + * do_something + * rescue FirstError, SecondError => e + * handle_error(e) + * end + * ``` + * This node is only present when there are two or more exceptions in the list. + */ +class ExceptionList extends Expr, TExceptionList { + private Ruby::Exceptions g; + + ExceptionList() { this = TExceptionList(g) } + + final override string getAPrimaryQlClass() { result = "ExceptionList" } + + /** Gets the `n`th exception in this list. */ + final Expr getException(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "..., ..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getException" and result = this.getException(_) + } +} + /** * A rescue clause. For example: * ```rb @@ -305,8 +336,16 @@ class RescueClause extends Expr, TRescueClause { * handle_error(e) * end * ``` + * When there are two or more exceptions, use `getExceptions()` to get the `ExceptionList` node. */ - final Expr getException(int n) { toGenerated(result) = g.getExceptions().getChild(n) } + final Expr getException(int n) { + // 0 or 1 exception: no ExceptionList node, access directly + not exists(this.getExceptions()) and + toGenerated(result) = g.getExceptions().getChild(n) + or + // 2+ exceptions: delegate through ExceptionList + result = this.getExceptions().getException(n) + } /** * Gets an exception to match, if any. For example `FirstError` or `SecondError` in: @@ -320,6 +359,19 @@ class RescueClause extends Expr, TRescueClause { */ final Expr getAnException() { result = this.getException(_) } + /** + * Gets the exception list node when there are two or more exceptions to match. For example, + * the exception list `FirstError, SecondError` in: + * ```rb + * begin + * do_something + * rescue FirstError, SecondError => e + * handle_error(e) + * end + * ``` + */ + final ExceptionList getExceptions() { result = TExceptionList(g.getExceptions()) } + /** * Gets the variable to which to assign the matched exception, if any. * For example `err` in: @@ -343,8 +395,13 @@ class RescueClause extends Expr, TRescueClause { final override AstNode getAChild(string pred) { result = super.getAChild(pred) or + // For 0 or 1 exceptions, exceptions are direct children + not exists(this.getExceptions()) and pred = "getException" and result = this.getException(_) or + // For 2+ exceptions, the ExceptionList node is the direct child + pred = "getExceptions" and result = this.getExceptions() + or pred = "getVariableExpr" and result = this.getVariableExpr() or pred = "getBody" and result = this.getBody() diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll index 4b3535c490d..1faba695a93 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll @@ -155,6 +155,7 @@ private module Cached { TEndBlock(Ruby::EndBlock g) or TEnsure(Ruby::Ensure g) or TEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequal } or + TExceptionList(Ruby::Exceptions g) { strictcount(g.getChild(_)) > 1 } or TExponentExprReal(Ruby::Binary g) { g instanceof @ruby_binary_starstar } or TExponentExprSynth(Ast::AstNode parent, int i) { mkSynthChild(ExponentExprKind(), parent, i) } or TFalseLiteral(Ruby::False g) or @@ -375,7 +376,8 @@ private module Cached { TClassVariableAccessReal or TComplementExpr or TComplexLiteral or TDefinedExprReal or TDelimitedSymbolLiteral or TDestructuredLeftAssignment or TDestructuredParameter or TDivExprReal or TDo or TDoBlock or TElementReference or TElseReal or TElsif or TEmptyStmt or - TEncoding or TEndBlock or TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or + TEncoding or TEndBlock or TEnsure or TEqExpr or TExceptionList or TExponentExprReal or + TFalseLiteral or TFile or TFindPattern or TFloatLiteral or TForExpr or TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExprReal or @@ -475,6 +477,7 @@ private module Cached { n = TEndBlock(result) or n = TEnsure(result) or n = TEqExpr(result) or + n = TExceptionList(result) or n = TExponentExprReal(result) or n = TFalseLiteral(result) or n = TFile(result) or @@ -765,7 +768,7 @@ class TExpr = TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or TCall or TBlockArgument or TConstantAccess or TControlExpr or TLiteral or TCallable or TVariableAccess or TStmtSequence or TOperation or TForwardArgument or TDestructuredLhsExpr or - TMatchPattern or TTestPattern; + TMatchPattern or TTestPattern or TExceptionList; class TSplatExpr = TSplatExprReal or TSplatExprSynth; diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 341f4cf2cd3..f41104f9e74 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -445,6 +445,16 @@ calls/calls.rb: # 255| getEnsure: [StmtSequence] ensure ... # 255| getStmt: [MethodCall] call to bar # 255| getReceiver: [ConstantReadAccess] X +# 257| getStmt: [BeginExpr] begin ... +# 258| getRescue: [RescueClause] rescue ... +# 258| getExceptions: [ExceptionList] ..., ... +# 258| getException: [MethodCall] call to foo +# 258| getReceiver: [SelfVariableAccess] self +# 258| getException: [MethodCall] call to bar +# 258| getReceiver: [ConstantReadAccess] X +# 259| getEnsure: [StmtSequence] ensure ... +# 259| getStmt: [MethodCall] call to baz +# 259| getReceiver: [SelfVariableAccess] self # 263| getStmt: [RescueModifierExpr] ... rescue ... # 263| getBody: [MethodCall] call to foo # 263| getReceiver: [SelfVariableAccess] self diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 49ebf74a90d..918a41fd035 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -715,11 +715,26 @@ calls/calls.rb: # 255| 1: [ReservedWord] :: # 255| 2: [Identifier] bar # 256| 3: [ReservedWord] end -# 263| 76: [RescueModifier] RescueModifier +# 257| 76: [Begin] Begin +# 257| 0: [ReservedWord] begin +# 258| 1: [Rescue] Rescue +# 258| 0: [ReservedWord] rescue +# 258| 1: [Exceptions] Exceptions +# 258| 0: [Identifier] foo +# 258| 1: [ReservedWord] , +# 258| 2: [Call] Call +# 258| 0: [Constant] X +# 258| 1: [ReservedWord] :: +# 258| 2: [Identifier] bar +# 259| 2: [Ensure] Ensure +# 259| 0: [ReservedWord] ensure +# 259| 1: [Identifier] baz +# 260| 3: [ReservedWord] end +# 263| 77: [RescueModifier] RescueModifier # 263| 0: [Identifier] foo # 263| 1: [ReservedWord] rescue # 263| 2: [Identifier] bar -# 264| 77: [RescueModifier] RescueModifier +# 264| 78: [RescueModifier] RescueModifier # 264| 0: [Call] Call # 264| 0: [Constant] X # 264| 1: [ReservedWord] :: @@ -729,7 +744,7 @@ calls/calls.rb: # 264| 0: [Constant] X # 264| 1: [ReservedWord] :: # 264| 2: [Identifier] bar -# 267| 78: [Call] Call +# 267| 79: [Call] Call # 267| 0: [Identifier] foo # 267| 1: [ArgumentList] ArgumentList # 267| 0: [ReservedWord] ( @@ -737,7 +752,7 @@ calls/calls.rb: # 267| 0: [ReservedWord] & # 267| 1: [Identifier] bar # 267| 2: [ReservedWord] ) -# 268| 79: [Call] Call +# 268| 80: [Call] Call # 268| 0: [Identifier] foo # 268| 1: [ArgumentList] ArgumentList # 268| 0: [ReservedWord] ( @@ -748,14 +763,14 @@ calls/calls.rb: # 268| 1: [ReservedWord] :: # 268| 2: [Identifier] bar # 268| 2: [ReservedWord] ) -# 269| 80: [Call] Call +# 269| 81: [Call] Call # 269| 0: [Identifier] foo # 269| 1: [ArgumentList] ArgumentList # 269| 0: [ReservedWord] ( # 269| 1: [BlockArgument] BlockArgument # 269| 0: [ReservedWord] & # 269| 2: [ReservedWord] ) -# 271| 81: [Call] Call +# 271| 82: [Call] Call # 271| 0: [Identifier] foo # 271| 1: [ArgumentList] ArgumentList # 271| 0: [ReservedWord] ( @@ -763,7 +778,7 @@ calls/calls.rb: # 271| 0: [ReservedWord] * # 271| 1: [Identifier] bar # 271| 2: [ReservedWord] ) -# 272| 82: [Call] Call +# 272| 83: [Call] Call # 272| 0: [Identifier] foo # 272| 1: [ArgumentList] ArgumentList # 272| 0: [ReservedWord] ( @@ -774,14 +789,14 @@ calls/calls.rb: # 272| 1: [ReservedWord] :: # 272| 2: [Identifier] bar # 272| 2: [ReservedWord] ) -# 273| 83: [Call] Call +# 273| 84: [Call] Call # 273| 0: [Identifier] foo # 273| 1: [ArgumentList] ArgumentList # 273| 0: [ReservedWord] ( # 273| 1: [SplatArgument] SplatArgument # 273| 0: [ReservedWord] * # 273| 2: [ReservedWord] ) -# 276| 84: [Call] Call +# 276| 85: [Call] Call # 276| 0: [Identifier] foo # 276| 1: [ArgumentList] ArgumentList # 276| 0: [ReservedWord] ( @@ -789,7 +804,7 @@ calls/calls.rb: # 276| 0: [ReservedWord] ** # 276| 1: [Identifier] bar # 276| 2: [ReservedWord] ) -# 277| 85: [Call] Call +# 277| 86: [Call] Call # 277| 0: [Identifier] foo # 277| 1: [ArgumentList] ArgumentList # 277| 0: [ReservedWord] ( @@ -800,14 +815,14 @@ calls/calls.rb: # 277| 1: [ReservedWord] :: # 277| 2: [Identifier] bar # 277| 2: [ReservedWord] ) -# 278| 86: [Call] Call +# 278| 87: [Call] Call # 278| 0: [Identifier] foo # 278| 1: [ArgumentList] ArgumentList # 278| 0: [ReservedWord] ( # 278| 1: [HashSplatArgument] HashSplatArgument # 278| 0: [ReservedWord] ** # 278| 2: [ReservedWord] ) -# 281| 87: [Call] Call +# 281| 88: [Call] Call # 281| 0: [Identifier] foo # 281| 1: [ArgumentList] ArgumentList # 281| 0: [ReservedWord] ( @@ -816,7 +831,7 @@ calls/calls.rb: # 281| 1: [ReservedWord] : # 281| 2: [Identifier] bar # 281| 2: [ReservedWord] ) -# 282| 88: [Call] Call +# 282| 89: [Call] Call # 282| 0: [Identifier] foo # 282| 1: [ArgumentList] ArgumentList # 282| 0: [ReservedWord] ( @@ -828,7 +843,7 @@ calls/calls.rb: # 282| 1: [ReservedWord] :: # 282| 2: [Identifier] bar # 282| 2: [ReservedWord] ) -# 287| 89: [Class] Class +# 287| 90: [Class] Class # 287| 0: [ReservedWord] class # 287| 1: [Constant] MyClass # 288| 2: [BodyStatement] BodyStatement @@ -923,7 +938,7 @@ calls/calls.rb: # 296| 3: [ReservedWord] end # 297| 3: [ReservedWord] end # 298| 3: [ReservedWord] end -# 304| 90: [Class] Class +# 304| 91: [Class] Class # 304| 0: [ReservedWord] class # 304| 1: [Constant] AnotherClass # 305| 2: [BodyStatement] BodyStatement @@ -945,27 +960,27 @@ calls/calls.rb: # 308| 2: [Identifier] super # 309| 3: [ReservedWord] end # 310| 3: [ReservedWord] end -# 313| 91: [Call] Call +# 313| 92: [Call] Call # 313| 0: [Identifier] foo # 313| 1: [ReservedWord] . # 313| 2: [ArgumentList] ArgumentList # 313| 0: [ReservedWord] ( # 313| 1: [ReservedWord] ) -# 314| 92: [Call] Call +# 314| 93: [Call] Call # 314| 0: [Identifier] foo # 314| 1: [ReservedWord] . # 314| 2: [ArgumentList] ArgumentList # 314| 0: [ReservedWord] ( # 314| 1: [Integer] 1 # 314| 2: [ReservedWord] ) -# 317| 93: [Assignment] Assignment +# 317| 94: [Assignment] Assignment # 317| 0: [Call] Call # 317| 0: [Self] self # 317| 1: [ReservedWord] . # 317| 2: [Identifier] foo # 317| 1: [ReservedWord] = # 317| 2: [Integer] 10 -# 318| 94: [Assignment] Assignment +# 318| 95: [Assignment] Assignment # 318| 0: [ElementReference] ElementReference # 318| 0: [Identifier] foo # 318| 1: [ReservedWord] [ @@ -973,7 +988,7 @@ calls/calls.rb: # 318| 3: [ReservedWord] ] # 318| 1: [ReservedWord] = # 318| 2: [Integer] 10 -# 319| 95: [Assignment] Assignment +# 319| 96: [Assignment] Assignment # 319| 0: [LeftAssignmentList] LeftAssignmentList # 319| 0: [Call] Call # 319| 0: [Self] self @@ -1003,7 +1018,7 @@ calls/calls.rb: # 319| 6: [ReservedWord] , # 319| 7: [Integer] 4 # 319| 8: [ReservedWord] ] -# 320| 96: [Assignment] Assignment +# 320| 97: [Assignment] Assignment # 320| 0: [LeftAssignmentList] LeftAssignmentList # 320| 0: [Identifier] a # 320| 1: [ReservedWord] , @@ -1023,14 +1038,14 @@ calls/calls.rb: # 320| 4: [ReservedWord] , # 320| 5: [Integer] 3 # 320| 6: [ReservedWord] ] -# 321| 97: [OperatorAssignment] OperatorAssignment +# 321| 98: [OperatorAssignment] OperatorAssignment # 321| 0: [Call] Call # 321| 0: [Self] self # 321| 1: [ReservedWord] . # 321| 2: [Identifier] count # 321| 1: [ReservedWord] += # 321| 2: [Integer] 1 -# 322| 98: [OperatorAssignment] OperatorAssignment +# 322| 99: [OperatorAssignment] OperatorAssignment # 322| 0: [ElementReference] ElementReference # 322| 0: [Identifier] foo # 322| 1: [ReservedWord] [ @@ -1038,7 +1053,7 @@ calls/calls.rb: # 322| 3: [ReservedWord] ] # 322| 1: [ReservedWord] += # 322| 2: [Integer] 1 -# 323| 99: [OperatorAssignment] OperatorAssignment +# 323| 100: [OperatorAssignment] OperatorAssignment # 323| 0: [ElementReference] ElementReference # 323| 0: [Call] Call # 323| 0: [Identifier] foo @@ -1062,12 +1077,12 @@ calls/calls.rb: # 323| 7: [ReservedWord] ] # 323| 1: [ReservedWord] *= # 323| 2: [Integer] 2 -# 326| 100: [Method] Method +# 326| 101: [Method] Method # 326| 0: [ReservedWord] def # 326| 1: [Identifier] foo # 326| 2: [ReservedWord] = # 326| 3: [Identifier] bar -# 327| 101: [Method] Method +# 327| 102: [Method] Method # 327| 0: [ReservedWord] def # 327| 1: [Identifier] foo # 327| 2: [MethodParameters] MethodParameters @@ -1075,7 +1090,7 @@ calls/calls.rb: # 327| 1: [ReservedWord] ) # 327| 3: [ReservedWord] = # 327| 4: [Identifier] bar -# 328| 102: [Method] Method +# 328| 103: [Method] Method # 328| 0: [ReservedWord] def # 328| 1: [Identifier] foo # 328| 2: [MethodParameters] MethodParameters @@ -1084,14 +1099,14 @@ calls/calls.rb: # 328| 2: [ReservedWord] ) # 328| 3: [ReservedWord] = # 328| 4: [Identifier] bar -# 329| 103: [SingletonMethod] SingletonMethod +# 329| 104: [SingletonMethod] SingletonMethod # 329| 0: [ReservedWord] def # 329| 1: [Constant] Object # 329| 2: [ReservedWord] . # 329| 3: [Identifier] foo # 329| 4: [ReservedWord] = # 329| 5: [Identifier] bar -# 330| 104: [SingletonMethod] SingletonMethod +# 330| 105: [SingletonMethod] SingletonMethod # 330| 0: [ReservedWord] def # 330| 1: [Constant] Object # 330| 2: [ReservedWord] . @@ -1102,7 +1117,7 @@ calls/calls.rb: # 330| 2: [ReservedWord] ) # 330| 5: [ReservedWord] = # 330| 6: [Identifier] bar -# 331| 105: [Method] Method +# 331| 106: [Method] Method # 331| 0: [ReservedWord] def # 331| 1: [Identifier] foo # 331| 2: [MethodParameters] MethodParameters @@ -1122,7 +1137,7 @@ calls/calls.rb: # 331| 1: [StringContent] error # 331| 2: [ReservedWord] " # 331| 2: [ReservedWord] ) -# 334| 106: [Method] Method +# 334| 107: [Method] Method # 334| 0: [ReservedWord] def # 334| 1: [Identifier] foo # 334| 2: [MethodParameters] MethodParameters @@ -1139,7 +1154,7 @@ calls/calls.rb: # 335| 0: [ReservedWord] ... # 335| 2: [ReservedWord] ) # 336| 4: [ReservedWord] end -# 338| 107: [Method] Method +# 338| 108: [Method] Method # 338| 0: [ReservedWord] def # 338| 1: [Identifier] foo # 338| 2: [MethodParameters] MethodParameters @@ -1162,7 +1177,7 @@ calls/calls.rb: # 339| 0: [ReservedWord] ... # 339| 4: [ReservedWord] ) # 340| 4: [ReservedWord] end -# 343| 108: [For] For +# 343| 109: [For] For # 343| 0: [ReservedWord] for # 343| 1: [LeftAssignmentList] LeftAssignmentList # 343| 0: [Identifier] x @@ -1202,7 +1217,7 @@ calls/calls.rb: # 344| 3: [ReservedWord] , # 344| 4: [Identifier] z # 345| 1: [ReservedWord] end -# 347| 109: [Call] Call +# 347| 110: [Call] Call # 347| 0: [Identifier] foo # 347| 1: [ArgumentList] ArgumentList # 347| 0: [ReservedWord] ( @@ -1211,7 +1226,7 @@ calls/calls.rb: # 347| 1: [ReservedWord] : # 347| 2: [Integer] 42 # 347| 2: [ReservedWord] ) -# 348| 110: [Call] Call +# 348| 111: [Call] Call # 348| 0: [Identifier] foo # 348| 1: [ArgumentList] ArgumentList # 348| 0: [ReservedWord] ( @@ -1223,7 +1238,7 @@ calls/calls.rb: # 348| 0: [HashKeySymbol] novar # 348| 1: [ReservedWord] : # 348| 4: [ReservedWord] ) -# 349| 111: [Call] Call +# 349| 112: [Call] Call # 349| 0: [Identifier] foo # 349| 1: [ArgumentList] ArgumentList # 349| 0: [ReservedWord] ( @@ -1232,7 +1247,7 @@ calls/calls.rb: # 349| 1: [ReservedWord] : # 349| 2: [Integer] 42 # 349| 2: [ReservedWord] ) -# 350| 112: [Call] Call +# 350| 113: [Call] Call # 350| 0: [Identifier] foo # 350| 1: [ArgumentList] ArgumentList # 350| 0: [ReservedWord] ( @@ -1240,11 +1255,11 @@ calls/calls.rb: # 350| 0: [HashKeySymbol] X # 350| 1: [ReservedWord] : # 350| 2: [ReservedWord] ) -# 353| 113: [Assignment] Assignment +# 353| 114: [Assignment] Assignment # 353| 0: [Identifier] y # 353| 1: [ReservedWord] = # 353| 2: [Integer] 1 -# 354| 114: [Assignment] Assignment +# 354| 115: [Assignment] Assignment # 354| 0: [Identifier] one # 354| 1: [ReservedWord] = # 354| 2: [Lambda] Lambda @@ -1258,7 +1273,7 @@ calls/calls.rb: # 354| 1: [BlockBody] BlockBody # 354| 0: [Identifier] y # 354| 2: [ReservedWord] } -# 355| 115: [Assignment] Assignment +# 355| 116: [Assignment] Assignment # 355| 0: [Identifier] f # 355| 1: [ReservedWord] = # 355| 2: [Lambda] Lambda @@ -1275,7 +1290,7 @@ calls/calls.rb: # 355| 1: [ArgumentList] ArgumentList # 355| 0: [Identifier] x # 355| 2: [ReservedWord] } -# 356| 116: [Assignment] Assignment +# 356| 117: [Assignment] Assignment # 356| 0: [Identifier] g # 356| 1: [ReservedWord] = # 356| 2: [Lambda] Lambda @@ -1289,7 +1304,7 @@ calls/calls.rb: # 356| 1: [BlockBody] BlockBody # 356| 0: [Identifier] unknown_call # 356| 2: [ReservedWord] } -# 357| 117: [Assignment] Assignment +# 357| 118: [Assignment] Assignment # 357| 0: [Identifier] h # 357| 1: [ReservedWord] = # 357| 2: [Lambda] Lambda @@ -1305,19 +1320,19 @@ calls/calls.rb: # 359| 1: [Identifier] y # 360| 2: [Identifier] unknown_call # 361| 2: [ReservedWord] end -# 364| 118: [Call] Call +# 364| 119: [Call] Call # 364| 0: [Identifier] list # 364| 1: [ReservedWord] . # 364| 2: [Identifier] empty? -# 365| 119: [Call] Call +# 365| 120: [Call] Call # 365| 0: [Identifier] list # 365| 1: [ReservedWord] &. # 365| 2: [Identifier] empty? -# 366| 120: [Call] Call +# 366| 121: [Call] Call # 366| 0: [Identifier] list # 366| 1: [ReservedWord] :: # 366| 2: [Identifier] empty? -# 367| 121: [Call] Call +# 367| 122: [Call] Call # 367| 0: [Identifier] foo # 367| 1: [ReservedWord] &. # 367| 2: [Identifier] bar diff --git a/ruby/ql/test/library-tests/ast/calls/calls.expected b/ruby/ql/test/library-tests/ast/calls/calls.expected index 4d9b7c9fdf8..a8c30218d24 100644 --- a/ruby/ql/test/library-tests/ast/calls/calls.expected +++ b/ruby/ql/test/library-tests/ast/calls/calls.expected @@ -282,6 +282,9 @@ callsWithReceiver | calls.rb:251:8:251:10 | call to bar | calls.rb:251:8:251:10 | self | | calls.rb:254:8:254:13 | call to foo | calls.rb:254:8:254:8 | X | | calls.rb:255:8:255:13 | call to bar | calls.rb:255:8:255:8 | X | +| calls.rb:258:8:258:10 | call to foo | calls.rb:258:8:258:10 | self | +| calls.rb:258:13:258:18 | call to bar | calls.rb:258:13:258:13 | X | +| calls.rb:259:8:259:10 | call to baz | calls.rb:259:8:259:10 | self | | calls.rb:263:1:263:3 | call to foo | calls.rb:263:1:263:3 | self | | calls.rb:263:12:263:14 | call to bar | calls.rb:263:12:263:14 | self | | calls.rb:264:1:264:6 | call to foo | calls.rb:264:1:264:1 | X | diff --git a/ruby/ql/test/library-tests/ast/calls/calls.rb b/ruby/ql/test/library-tests/ast/calls/calls.rb index 32cc6979179..34beb26ba03 100644 --- a/ruby/ql/test/library-tests/ast/calls/calls.rb +++ b/ruby/ql/test/library-tests/ast/calls/calls.rb @@ -254,10 +254,10 @@ begin rescue X::foo ensure X::bar end - - - - +begin +rescue foo, X::bar +ensure baz +end # rescue-modifier body and handler foo rescue bar