Skip to content

Ruby: synthesize implicit true value for valueless CaseExpr#22043

Open
Copilot wants to merge 2 commits into
mainfrom
copilot/tweak-ruby-ast-caseexpr
Open

Ruby: synthesize implicit true value for valueless CaseExpr#22043
Copilot wants to merge 2 commits into
mainfrom
copilot/tweak-ruby-ast-caseexpr

Conversation

Copilot AI commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

A Ruby case/when expression without a subject (e.g. case; when x > 0; ...) has no getValue(). Semantically this is equivalent to case true; when x > 0; ..., but the AST didn't reflect this. This synthesizes an explicit BooleanLiteral(true) so that CaseExpr#getValue() always holds.

Changes

  • ast/internal/Synthesis.qll: Added CaseNoValueSynthesis module — for any TCaseExpr(g) where g.getValue() is absent, synthesizes a BooleanLiteralKind(true) child at index -2 (negative to avoid colliding with real grammar child indices used by branch positions).

  • ast/internal/Control.qll: Updated CaseWhenClause.getValue() to include synthChild(this, -2, result) as a fallback when no real value exists.

Downstream effects on tests

  • CaseExpr.expected: the previously-valueless case now appears in caseValues; caseNoValues is empty.
  • CFG (Cfg.expected, BasicBlocks.expected, barrier-guards.expected): when-clause completions for valueless cases now use [match]/[no-match] instead of [true]/[false], consistent with valued case expressions — because Completion.qll keys off exists(c.getValue()) to decide between boolean vs. matching context.
  • Ast.expected, ValueText.expected: updated to include the synthesized true literal.
# Before: getValue() had no result
# After:  getValue() returns synthesized `true`
case
when a > b then 10
when a < b then 30
end

@aschackmull aschackmull marked this pull request as ready for review June 24, 2026 11:37
@aschackmull aschackmull requested a review from a team as a code owner June 24, 2026 11:37
Copilot AI review requested due to automatic review settings June 24, 2026 11:37
@aschackmull aschackmull added the no-change-note-required This PR does not need a change note label Jun 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CaseExpr value by creating a synthesized BooleanLiteral(true).
  • Update CaseWhenClause.getValue() to fall back to the synthesized child when no real subject value exists.
  • Refresh multiple Ruby library-test .expected baselines 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 valueless case expression changes from “no result” to returning a synthesized true literal, which can break existing queries that detect valueless cases via not exists(c.getValue()). This kind of public AST API behavior change should be documented in a Ruby change note (likely category: breaking), similar to the recent CaseElseBranch change 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-change-note-required This PR does not need a change note Ruby

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants