Ruby AST: preserve ExceptionList node in RescueClause for 2+ exceptions#22014
Conversation
946b770 to
6fbb572
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates the Ruby AST modeling to preserve the intermediate tree-sitter Exceptions node for rescue clauses that specify two or more exception types, by introducing a new ExceptionList AST node. This helps maintain the original syntactic structure instead of flattening exception types directly under RescueClause.
Changes:
- Introduces a new
ExceptionListAST node forrescueclauses with 2+ exceptions and exposes it viaRescueClause.getExceptions(). - Updates
RescueClause.getException(int)to remain usable across 0/1/2+ exception cases by delegating throughExceptionListfor 2+. - Extends library tests/fixtures to include a
rescueclause with multiple exceptions and updates expected outputs accordingly.
Show a summary per file
| File | Description |
|---|---|
| ruby/ql/lib/codeql/ruby/ast/internal/AST.qll | Adds internal TExceptionList node kind and wires it into the generated AST node sets. |
| ruby/ql/lib/codeql/ruby/ast/Expr.qll | Introduces ExceptionList and updates RescueClause APIs/child edges for 2+ exceptions. |
| ruby/ql/test/library-tests/ast/calls/calls.rb | Adds a begin … rescue foo, X::bar … ensure … end fixture to exercise the 2+ exception case. |
| ruby/ql/test/library-tests/ast/Ast.expected | Updates printed AST expectations to include the new ExceptionList node/edge. |
| ruby/ql/test/library-tests/ast/TreeSitter.expected | Updates tree-sitter parse-tree expectations for the modified fixture file layout. |
| ruby/ql/test/library-tests/ast/ValueText.expected | Updates constant-value expectations due to fixture line shifts. |
| ruby/ql/test/library-tests/ast/calls/calls.expected | Updates call expectations due to fixture line shifts/new construct. |
| ruby/ql/test/library-tests/ast/calls/arguments.expected | Updates argument expectations due to fixture line shifts. |
| ruby/ql/test/library-tests/ast/AstDesugar.expected | Updates desugaring expectations due to fixture line shifts. |
Copilot's findings
- Files reviewed: 9/9 changed files
- Comments generated: 0
|
For review: Copilot updated a qltest, which shifted a lot of line numbers. So I injected an initial commit that just does the trivial line number updates in order to separate the actual test changes. |
| 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 |
There was a problem hiding this comment.
Wouldn't it be cleaner to always synthesize this node? Then we can also simplify some logic AFAICT.
There was a problem hiding this comment.
My thinking was that it would represent a disjunctive pattern. So when you get the pattern of the catch clause then it's either a single exception expression or a disjunctive pattern with multiple nested exception expressions. With that point of view, then I think it would be confusing to add singleton disjunctive patterns. But we should perhaps replace the getExceptions member with a getPattern member that always has a result to reinforce that view.
There was a problem hiding this comment.
I've pushed a tweak that should hopefully make the intent clearer.
There was a problem hiding this comment.
Thanks, that's much better.
RescueClausepreviously flattened all exception types as direct children, discarding the intermediateExceptionstree-sitter node. For 2+ exceptions, a newExceptionListAST node now preserves that structure.Behavior
getException(0)ExceptionListis a direct child viagetExceptions(); individual exceptions accessed viaExceptionList.getException(n)RescueClause.getException(int n)andgetAnException()remain usable for all cases (delegates throughExceptionListfor 2+). The CFG is unaffected.Changes
AST.qll: AddedTExceptionList(Ruby::Exceptions g)(only whenstrictcount > 1); added toTAstNodeReal,toGenerated, andTExprExpr.qll: NewExceptionListclass withgetException(int n);RescueClause.getExceptiondelegates throughExceptionListfor 2+ case; newRescueClause.getExceptions();getAChildupdated to exposeExceptionListas the tree edge for 2+ case