mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge pull request #305 from github/hvitved/desugar/array-literals
Desugar array literals to `::Array.[]`
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(...)" }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() |
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user