Ruby: desugar one-line pattern matches

This commit is contained in:
Arthur Baars
2023-02-03 18:49:56 +01:00
parent 3c15fd266d
commit 4af0c4bb03
6 changed files with 213 additions and 31 deletions

View File

@@ -478,15 +478,11 @@ class WhenClause extends AstNode, TWhenClause {
* end
* ```
*/
class InClause extends AstNode, TInClause {
private Ruby::InClause g;
InClause() { this = TInClause(g) }
class InClause extends AstNode instanceof InClauseImpl {
final override string getAPrimaryQlClass() { result = "InClause" }
/** Gets the body of this case-in expression. */
final Stmt getBody() { toGenerated(result) = g.getBody() }
final Stmt getBody() { result = super.getBody() }
/**
* Gets the pattern in this case-in expression. In the
@@ -498,7 +494,7 @@ class InClause extends AstNode, TInClause {
* end
* ```
*/
final CasePattern getPattern() { toGenerated(result) = g.getPattern() }
final CasePattern getPattern() { result = super.getPattern() }
/**
* Gets the pattern guard condition in this case-in expression. In the
@@ -510,7 +506,7 @@ class InClause extends AstNode, TInClause {
* end
* ```
*/
final Expr getCondition() { toGenerated(result) = g.getGuard().getAFieldOrChild() }
final Expr getCondition() { result = super.getCondition() }
/**
* Holds if the pattern guard in this case-in expression is an `if` condition. For example:
@@ -520,7 +516,7 @@ class InClause extends AstNode, TInClause {
* end
* ```
*/
predicate hasIfCondition() { g.getGuard() instanceof Ruby::IfGuard }
predicate hasIfCondition() { super.hasIfCondition() }
/**
* Holds if the pattern guard in this case-in expression is an `unless` condition. For example:
@@ -530,12 +526,12 @@ class InClause extends AstNode, TInClause {
* end
* ```
*/
predicate hasUnlessCondition() { g.getGuard() instanceof Ruby::UnlessGuard }
predicate hasUnlessCondition() { super.hasUnlessCondition() }
final override string toString() { result = "in ... then ..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
result = AstNode.super.getAChild(pred)
or
pred = "getBody" and result = this.getBody()
or

View File

@@ -97,12 +97,16 @@ private module Cached {
} or
TBlockArgument(Ruby::BlockArgument g) or
TBlockParameter(Ruby::BlockParameter g) or
TBooleanLiteralSynth(Ast::AstNode parent, int i, boolean value) {
mkSynthChild(BooleanLiteralKind(value), parent, i)
} or
TBraceBlockSynth(Ast::AstNode parent, int i) { mkSynthChild(BraceBlockKind(), parent, i) } or
TBraceBlockReal(Ruby::Block g) { not g.getParent() instanceof Ruby::Lambda } or
TBreakStmt(Ruby::Break g) or
TCaseEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequalequal } or
TCaseExpr(Ruby::Case g) or
TCaseMatch(Ruby::CaseMatch g) or
TCaseMatchReal(Ruby::CaseMatch g) or
TCaseMatchSynth(Ast::AstNode parent, int i) { mkSynthChild(CaseMatchKind(), parent, i) } or
TCharacterLiteral(Ruby::Character g) or
TClassDeclaration(Ruby::Class g) or
TClassVariableAccessReal(Ruby::ClassVariable g, Ast::ClassVariable v) {
@@ -130,6 +134,7 @@ private module Cached {
TDoBlock(Ruby::DoBlock g) { not g.getParent() instanceof Ruby::Lambda } or
TElementReference(Ruby::ElementReference g) or
TElse(Ruby::Else g) or
TElseSynth(Ast::AstNode parent, int i) { mkSynthChild(ElseKind(), parent, i) } or
TElsif(Ruby::Elsif g) or
TEmptyStmt(Ruby::EmptyStatement g) or
TEncoding(Ruby::Encoding g) or
@@ -168,7 +173,8 @@ private module Cached {
TIfReal(Ruby::If g) or
TIfSynth(Ast::AstNode parent, int i) { mkSynthChild(IfKind(), parent, i) } or
TIfModifierExpr(Ruby::IfModifier g) or
TInClause(Ruby::InClause g) or
TInClauseReal(Ruby::InClause g) or
TInClauseSynth(Ast::AstNode parent, int i) { mkSynthChild(InClauseKind(), parent, i) } or
TInstanceVariableAccessReal(Ruby::InstanceVariable g, Ast::InstanceVariable v) {
InstanceVariableAccess::range(g, v)
} or
@@ -346,7 +352,7 @@ private module Cached {
TAssignMulExpr or TAssignRShiftExpr or TAssignSubExpr or TBareStringLiteral or
TBareSymbolLiteral or TBeginBlock or TBeginExpr or TBitwiseAndExprReal or
TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or TBlockParameter or
TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or TCaseMatch or
TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or TCaseMatchReal or
TCharacterLiteral or TClassDeclaration or TClassVariableAccessReal or TComplementExpr or
TComplexLiteral or TDefinedExpr or TDelimitedSymbolLiteral or TDestructuredLeftAssignment or
TDestructuredParameter or TDivExprReal or TDo or TDoBlock or TElementReference or TElse or
@@ -355,7 +361,7 @@ private module Cached {
TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or
THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExpr or
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or
TIfReal or TIfModifierExpr or TInClause or TInstanceVariableAccessReal or
TIfReal or TIfModifierExpr or TInClauseReal or TInstanceVariableAccessReal or
TIntegerLiteralReal or TKeywordParameter or TLEExpr or TLShiftExprReal or TLTExpr or
TLambda or TLeftAssignmentList or TLine or TLocalVariableAccessReal or
TLogicalAndExprReal or TLogicalOrExprReal or TMethod or TMatchPattern or
@@ -377,14 +383,14 @@ private module Cached {
class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or
TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or
TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or
TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or
TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or
TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
TBitwiseXorExprSynth or TBraceBlockSynth or TBooleanLiteralSynth or TCaseMatchSynth or
TClassVariableAccessSynth or TConstantReadAccessSynth or TConstantWriteAccessSynth or
TDivExprSynth or TElseSynth or TExponentExprSynth or TGlobalVariableAccessSynth or
TIfSynth or TInClauseSynth or TInstanceVariableAccessSynth or TIntegerLiteralSynth or
TLShiftExprSynth or TLocalVariableAccessSynth or TLogicalAndExprSynth or
TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or TMulExprSynth or
TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or
TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
/**
* Gets the underlying TreeSitter entity for a given AST node. This does not
@@ -426,7 +432,7 @@ private module Cached {
n = TBreakStmt(result) or
n = TCaseEqExpr(result) or
n = TCaseExpr(result) or
n = TCaseMatch(result) or
n = TCaseMatchReal(result) or
n = TCharacterLiteral(result) or
n = TClassDeclaration(result) or
n = TClassVariableAccessReal(result, _) or
@@ -467,7 +473,7 @@ private module Cached {
n = TIdentifierMethodCall(result) or
n = TIfModifierExpr(result) or
n = TIfReal(result) or
n = TInClause(result) or
n = TInClauseReal(result) or
n = TInstanceVariableAccessReal(result, _) or
n = TIntegerLiteralReal(result) or
n = TKeywordParameter(result) or
@@ -567,8 +573,12 @@ private module Cached {
or
result = TBitwiseXorExprSynth(parent, i)
or
result = TBooleanLiteralSynth(parent, i, _)
or
result = TBraceBlockSynth(parent, i)
or
result = TCaseMatchSynth(parent, i)
or
result = TClassVariableAccessSynth(parent, i, _)
or
result = TConstantReadAccessSynth(parent, i, _)
@@ -577,12 +587,16 @@ private module Cached {
or
result = TDivExprSynth(parent, i)
or
result = TElseSynth(parent, i)
or
result = TExponentExprSynth(parent, i)
or
result = TGlobalVariableAccessSynth(parent, i, _)
or
result = TIfSynth(parent, i)
or
result = TInClauseSynth(parent, i)
or
result = TInstanceVariableAccessSynth(parent, i, _)
or
result = TIntegerLiteralSynth(parent, i, _)
@@ -673,8 +687,12 @@ TAstNodeReal fromGenerated(Ruby::AstNode n) { n = toGenerated(result) }
class TCall = TMethodCall or TYieldCall;
class TCaseMatch = TCaseMatchReal or TCaseMatchSynth;
class TCase = TCaseExpr or TCaseMatch;
class TInClause = TInClauseReal or TInClauseSynth;
class TMethodCall =
TMethodCallSynth or TIdentifierMethodCall or TRegularMethodCall or TElementReference or
TSuperCall or TUnaryOperation or TBinaryOperation;
@@ -685,7 +703,7 @@ class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or
TConstantReadAccessSynth or TConstantWriteAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatchReal or TCaseMatchSynth or TLoop;
class TConditionalExpr =
TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr;
@@ -711,8 +729,9 @@ class TExpr =
class TSplatExpr = TSplatExprReal or TSplatExprSynth;
class TStmtSequence =
TBeginBlock or TEndBlock or TThen or TElse or TDo or TEnsure or TStringInterpolationComponent or
TBlock or TBodyStmt or TParenthesizedExpr or TStmtSequenceSynth;
TBeginBlock or TEndBlock or TThen or TElse or TElseSynth or TDo or TEnsure or
TStringInterpolationComponent or TBlock or TBodyStmt or TParenthesizedExpr or
TStmtSequenceSynth;
class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod;
@@ -727,7 +746,7 @@ class TNumericLiteral = TIntegerLiteral or TFloatLiteral or TRationalLiteral or
class TIntegerLiteral = TIntegerLiteralReal or TIntegerLiteralSynth;
class TBooleanLiteral = TTrueLiteral or TFalseLiteral;
class TBooleanLiteral = TTrueLiteral or TFalseLiteral or TBooleanLiteralSynth;
class TStringTextComponentNonRegexp =
TStringTextComponentNonRegexpStringOrHeredocContent or

View File

@@ -21,10 +21,10 @@ class CaseWhenClause extends CaseExprImpl, TCaseExpr {
}
}
class CaseMatch extends CaseExprImpl, TCaseMatch {
class CaseMatch extends CaseExprImpl, TCaseMatchReal {
private Ruby::CaseMatch g;
CaseMatch() { this = TCaseMatch(g) }
CaseMatch() { this = TCaseMatchReal(g) }
final override Expr getValue() { toGenerated(result) = g.getValue() }
@@ -34,3 +34,53 @@ class CaseMatch extends CaseExprImpl, TCaseMatch {
n = count(g.getClauses(_)) and toGenerated(result) = g.getElse()
}
}
class CaseMatchSynth extends CaseExprImpl, TCaseMatchSynth {
CaseMatchSynth() { this = TCaseMatchSynth(_, _) }
final override Expr getValue() { synthChild(this, 0, result) }
final override AstNode getBranch(int n) { n >= 0 and synthChild(this, n + 1, result) }
}
abstract class InClauseImpl extends AstNode, TInClause {
abstract Stmt getBody();
abstract CasePattern getPattern();
Expr getCondition() { none() }
predicate hasIfCondition() { none() }
predicate hasUnlessCondition() { none() }
}
class InClauseReal extends InClauseImpl, TInClauseReal {
private Ruby::InClause g;
InClauseReal() { this = TInClauseReal(g) }
final override Stmt getBody() { toGenerated(result) = g.getBody() }
final override CasePattern getPattern() { toGenerated(result) = g.getPattern() }
final override Expr getCondition() { toGenerated(result) = g.getGuard().getAFieldOrChild() }
final override predicate hasIfCondition() { g.getGuard() instanceof Ruby::IfGuard }
final override predicate hasUnlessCondition() { g.getGuard() instanceof Ruby::UnlessGuard }
}
class InClauseSynth extends InClauseImpl, TInClauseSynth {
InClauseSynth() { this = TInClauseSynth(_, _) }
final override Stmt getBody() { synthChild(this, 1, result) }
final override CasePattern getPattern() { synthChild(this, 0, result) }
final override Expr getCondition() { none() }
final override predicate hasIfCondition() { none() }
final override predicate hasUnlessCondition() { none() }
}

View File

@@ -29,6 +29,14 @@ class Else extends StmtSequence, TElse {
final override string toString() { result = "else ..." }
}
class ElseSynth extends StmtSequence, TElseSynth {
ElseSynth() { this = TElseSynth(_, _) }
override Stmt getStmt(int n) { synthChild(this, n, result) }
final override string toString() { result = "else ..." }
}
class Do extends StmtSequence, TDo {
private Ruby::Do g;

View File

@@ -149,6 +149,12 @@ class FalseLiteral extends BooleanLiteralImpl, TFalseLiteral {
final override boolean getValue() { result = false }
}
class BooleanLiteralSynth extends BooleanLiteralImpl, TBooleanLiteralSynth {
final override string toString() { result = this.getValue().toString() }
final override boolean getValue() { this = TBooleanLiteralSynth(_, _, result) }
}
class EncodingLiteralImpl extends Expr, TEncoding {
private Ruby::Encoding g;

View File

@@ -16,12 +16,16 @@ newtype SynthKind =
BitwiseAndExprKind() or
BitwiseOrExprKind() or
BitwiseXorExprKind() or
BooleanLiteralKind(boolean value) { value = true or value = false } or
BraceBlockKind() or
CaseMatchKind() or
ClassVariableAccessKind(ClassVariable v) or
DivExprKind() or
ElseKind() or
ExponentExprKind() or
GlobalVariableAccessKind(GlobalVariable v) or
IfKind() or
InClauseKind() or
InstanceVariableAccessKind(InstanceVariable v) or
IntegerLiteralKind(int i) { i in [-1000 .. 1000] } or
LShiftExprKind() or
@@ -1497,3 +1501,102 @@ private module SafeNavigationCallDesugar {
}
}
}
private module TestPatternDesugar {
/**
* ```rb
* expr in pattern
* ```
* desugars to
*
* ```rb
* case expr
* in pattern then true
* else false
* end
* ```
*/
pragma[nomagic]
private predicate testPatternSynthesis(AstNode parent, int i, Child child) {
exists(TestPattern test |
parent = test and
i = -1 and
child = SynthChild(CaseMatchKind())
or
exists(TCaseMatchSynth case | case = TCaseMatchSynth(test, -1) |
parent = case and
(
child = childRef(test.getValue()) and i = 0
or
child = SynthChild(InClauseKind()) and i = 1
or
child = SynthChild(ElseKind()) and i = 2
)
or
parent = TInClauseSynth(case, 1) and
(
child = childRef(test.getPattern()) and
i = 0
or
child = SynthChild(BooleanLiteralKind(true)) and i = 1
)
or
parent = TElseSynth(case, 2) and
child = SynthChild(BooleanLiteralKind(false)) and
i = 0
)
)
}
private class TestPatternSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
testPatternSynthesis(parent, i, child)
}
}
}
private module MatchPatternDesugar {
/**
* ```rb
* expr => pattern
* ```
* desugars to
*
* ```rb
* case expr
* in pattern then nil
* end
* ```
*/
pragma[nomagic]
private predicate matchPatternSynthesis(AstNode parent, int i, Child child) {
exists(MatchPattern test |
parent = test and
i = -1 and
child = SynthChild(CaseMatchKind())
or
exists(TCaseMatchSynth case | case = TCaseMatchSynth(test, -1) |
parent = case and
(
child = childRef(test.getValue()) and i = 0
or
child = SynthChild(InClauseKind()) and i = 1
)
or
parent = TInClauseSynth(case, 1) and
(
child = childRef(test.getPattern()) and
i = 0
or
child = SynthChild(NilLiteralKind()) and i = 1
)
)
)
}
private class MatchPatternSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
matchPatternSynthesis(parent, i, child)
}
}
}