Merge pull request #305 from github/hvitved/desugar/array-literals

Desugar array literals to `::Array.[]`
This commit is contained in:
Tom Hvitved
2021-09-23 17:30:34 +02:00
committed by GitHub
14 changed files with 378 additions and 69 deletions

View File

@@ -70,6 +70,20 @@ private class ScopeResolutionConstantAccess extends ConstantAccess, TScopeResolu
final override predicate hasGlobalScope() { not exists(g.getScope()) }
}
private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAccessSynth {
private string value;
ConstantReadAccessSynth() { this = TConstantReadAccessSynth(_, _, value) }
final override string getName() {
if this.hasGlobalScope() then result = value.suffix(2) else result = value
}
final override Expr getScopeExpr() { synthChild(this, 0, result) }
final override predicate hasGlobalScope() { value.matches("::%") }
}
/**
* A use (read) of a constant.
*
@@ -92,6 +106,8 @@ class ConstantReadAccess extends ConstantAccess {
or
// `X` in `X ||= 10` is considered both a read and a write
this = any(AssignOperation a).getLeftOperand()
or
this instanceof TConstantReadAccessSynth
}
/**

View File

@@ -678,13 +678,13 @@ class ArrayLiteral extends Literal, TArrayLiteral {
final override string getAPrimaryQlClass() { result = "ArrayLiteral" }
/** Gets the `n`th element in this array literal. */
Expr getElement(int n) { none() }
final Expr getElement(int n) { result = this.(ArrayLiteralImpl).getElementImpl(n) }
/** Gets an element in this array literal. */
final Expr getAnElement() { result = this.getElement(_) }
/** Gets the number of elements in this array literal. */
final int getNumberOfElements() { result = count(this.getAnElement()) }
final int getNumberOfElements() { result = this.(ArrayLiteralImpl).getNumberOfElementsImpl() }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
@@ -693,32 +693,44 @@ class ArrayLiteral extends Literal, TArrayLiteral {
}
}
private class RegularArrayLiteral extends ArrayLiteral, TRegularArrayLiteral {
abstract private class ArrayLiteralImpl extends ArrayLiteral {
abstract Expr getElementImpl(int n);
abstract int getNumberOfElementsImpl();
}
private class RegularArrayLiteral extends ArrayLiteralImpl, TRegularArrayLiteral {
private Ruby::Array g;
RegularArrayLiteral() { this = TRegularArrayLiteral(g) }
final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) }
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
final override string toString() { result = "[...]" }
}
private class StringArrayLiteral extends ArrayLiteral, TStringArrayLiteral {
private class StringArrayLiteral extends ArrayLiteralImpl, TStringArrayLiteral {
private Ruby::StringArray g;
StringArrayLiteral() { this = TStringArrayLiteral(g) }
final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) }
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
final override string toString() { result = "%w(...)" }
}
private class SymbolArrayLiteral extends ArrayLiteral, TSymbolArrayLiteral {
private class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral {
private Ruby::SymbolArray g;
SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) }
final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) }
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
final override string toString() { result = "%i(...)" }
}

View File

@@ -107,6 +107,9 @@ private module Cached {
} or
TComplementExpr(Ruby::Unary g) { g instanceof @ruby_unary_tilde } or
TComplexLiteral(Ruby::Complex g) or
TConstantReadAccessSynth(AST::AstNode parent, int i, string value) {
mkSynthChild(ConstantReadAccessKind(value), parent, i)
} or
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
@@ -439,6 +442,8 @@ private module Cached {
or
result = TClassVariableAccessSynth(parent, i, _)
or
result = TConstantReadAccessSynth(parent, i, _)
or
result = TDivExprSynth(parent, i)
or
result = TExponentExprSynth(parent, i)
@@ -526,7 +531,8 @@ class TMethodCall =
class TSuperCall = TTokenSuperCall or TRegularSuperCall;
class TConstantAccess = TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace;
class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TLoop;

View File

@@ -35,7 +35,8 @@ newtype SynthKind =
SplatExprKind() or
StmtSequenceKind() or
SelfKind() or
SubExprKind()
SubExprKind() or
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) }
/**
* An AST child.
@@ -77,6 +78,11 @@ class Synthesis extends TSynthesis {
*/
predicate methodCall(string name, boolean setter, int arity) { none() }
/**
* Holds if a constant read access of `name` is needed.
*/
predicate constantReadAccess(string name) { none() }
/**
* Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
*/
@@ -373,7 +379,7 @@ private module AssignOperationDesugar {
* x += y
* ```
*
* desguars to
* desugars to
*
* ```rb
* x = x + y
@@ -503,7 +509,7 @@ private module AssignOperationDesugar {
* foo[bar] += y
* ```
*
* desguars to
* desugars to
*
* ```rb
* __synth__0 = foo;
@@ -702,7 +708,7 @@ private module CompoundAssignDesugar {
* ```rb
* x, *y, z = w
* ```
* desguars to
* desugars to
*
* ```rb
* __synth__0 = *w;
@@ -745,3 +751,47 @@ private module CompoundAssignDesugar {
}
}
}
private module ArrayLiteralDesugar {
pragma[nomagic]
private predicate arrayLiteralSynthesis(AstNode parent, int i, Child child) {
exists(ArrayLiteral al |
parent = al and
i = -1 and
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements() + 1))
or
exists(AstNode mc | mc = TMethodCallSynth(al, -1, _, _, _) |
parent = mc and
i = 0 and
child = SynthChild(ConstantReadAccessKind("::Array"))
or
parent = mc and
child = RealChild(al.getElement(i - 1))
)
)
}
/**
* ```rb
* [1, 2, 3]
* ```
* desugars to
*
* ```rb
* ::Array.[](1, 2, 3)
* ```
*/
private class CompoundAssignSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
arrayLiteralSynthesis(parent, i, child)
}
final override predicate methodCall(string name, boolean setter, int arity) {
name = "[]" and
setter = false and
arity = any(ArrayLiteral al).getNumberOfElements() + 1
}
final override predicate constantReadAccess(string name) { name = "::Array" }
}
}

View File

@@ -130,10 +130,6 @@ module Trees {
}
}
private class ArrayLiteralTree extends StandardPostOrderTree, ArrayLiteral {
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
}
private class AssignExprTree extends StandardPostOrderTree, AssignExpr {
AssignExprTree() {
exists(Expr left | left = this.getLeftOperand() |

View File

@@ -236,7 +236,14 @@ private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) {
or
result.asExpr().getExpr() instanceof StringlikeLiteral and tp = TResolved("String")
or
result.asExpr().getExpr() instanceof ArrayLiteral and tp = TResolved("Array")
exists(ConstantReadAccess array, MethodCall mc |
result.asExpr().getExpr() = mc and
mc.getMethodName() = "[]" and
mc.getReceiver() = array and
array.getName() = "Array" and
array.hasGlobalScope() and
tp = TResolved("Array")
)
or
result.asExpr().getExpr() instanceof HashLiteral and tp = TResolved("Hash")
or