Ruby: synthesize implicit true value for valueless CaseExpr#22043
Open
Copilot wants to merge 2 commits into
Open
Ruby: synthesize implicit true value for valueless CaseExpr#22043Copilot wants to merge 2 commits into
true value for valueless CaseExpr#22043Copilot wants to merge 2 commits into
Conversation
Copilot created this pull request from a session on behalf of
aschackmull
June 24, 2026 11:03
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Ruby AST modeling so that a valueless case/when expression (for example, case; when x > 0; ...) is represented as having an explicit synthesized true value, aligning the AST with Ruby semantics (case true; when ...).
Changes:
- Add AST synthesis for a missing
CaseExprvalue by creating a synthesizedBooleanLiteral(true). - Update
CaseWhenClause.getValue()to fall back to the synthesized child when no real subject value exists. - Refresh multiple Ruby library-test
.expectedbaselines to reflect the new AST/CFG behavior.
Show a summary per file
| File | Description |
|---|---|
| ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected | Updates expected control-flow “when” completions to use match/no-match semantics for valueless case. |
| ruby/ql/test/library-tests/controlflow/graph/Cfg.expected | Updates CFG expectations to include synthesized true value and match/no-match completions. |
| ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected | Updates basic block dominance/post-dominance expectations due to synthesized case value and completion kind changes. |
| ruby/ql/test/library-tests/ast/ValueText.expected | Adds the synthesized true literal to expected value-text output. |
| ruby/ql/test/library-tests/ast/control/CaseExpr.expected | Moves previously-valueless case instances into caseValues (value is now synthesized). |
| ruby/ql/test/library-tests/ast/Ast.expected | Updates AST shape expectations to show CaseExpr.getValue as a BooleanLiteral(true). |
| ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll | Introduces synthesis logic for implicit true value on valueless case/when. |
| ruby/ql/lib/codeql/ruby/ast/internal/Control.qll | Makes CaseWhenClause.getValue() fall back to the synthesized value when needed. |
| ruby/ql/lib/codeql/ruby/ast/Control.qll | Updates public API documentation for CaseExpr.getValue() to reflect synthesized true for valueless case. |
Copilot's findings
Comments suppressed due to low confidence (1)
ruby/ql/lib/codeql/ruby/ast/Control.qll:362
CaseExpr.getValue()for a valuelesscaseexpression changes from “no result” to returning a synthesizedtrueliteral, which can break existing queries that detect valueless cases vianot exists(c.getValue()). This kind of public AST API behavior change should be documented in a Ruby change note (likelycategory: breaking), similar to the recentCaseElseBranchchange note.
* Gets the expression being compared. For example, `foo` in the following example.
* ```rb
* case foo
* when 0
* puts 'zero'
- Files reviewed: 9/9 changed files
- Comments generated: 1
| private module CaseNoValueSynthesis { | ||
| pragma[nomagic] | ||
| private predicate caseNoValueSynthesis(AstNode parent, int i, Child child) { | ||
| // Synthesize a `true` literal as the value of a `case`/`when` expression that has no value |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Ruby
case/whenexpression without a subject (e.g.case; when x > 0; ...) has nogetValue(). Semantically this is equivalent tocase true; when x > 0; ..., but the AST didn't reflect this. This synthesizes an explicitBooleanLiteral(true)so thatCaseExpr#getValue()always holds.Changes
ast/internal/Synthesis.qll: AddedCaseNoValueSynthesismodule — for anyTCaseExpr(g)whereg.getValue()is absent, synthesizes aBooleanLiteralKind(true)child at index-2(negative to avoid colliding with real grammar child indices used by branch positions).ast/internal/Control.qll: UpdatedCaseWhenClause.getValue()to includesynthChild(this, -2, result)as a fallback when no real value exists.Downstream effects on tests
CaseExpr.expected: the previously-valueless case now appears incaseValues;caseNoValuesis empty.Cfg.expected,BasicBlocks.expected,barrier-guards.expected):when-clause completions for valueless cases now use[match]/[no-match]instead of[true]/[false], consistent with valuedcaseexpressions — becauseCompletion.qllkeys offexists(c.getValue())to decide between boolean vs. matching context.Ast.expected,ValueText.expected: updated to include the synthesizedtrueliteral.