mirror of
https://github.com/github/codeql.git
synced 2026-05-13 18:59:27 +02:00
Merge pull request #10918 from asgerf/rb/constant-compound-assignment
Ruby: handle compound constant-assignment
This commit is contained in:
@@ -252,6 +252,20 @@ private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAcces
|
||||
final override predicate hasGlobalScope() { value.matches("::%") }
|
||||
}
|
||||
|
||||
private class ConstantWriteAccessSynth extends ConstantAccess, TConstantWriteAccessSynth {
|
||||
private string value;
|
||||
|
||||
ConstantWriteAccessSynth() { this = TConstantWriteAccessSynth(_, _, 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.
|
||||
*
|
||||
@@ -323,7 +337,9 @@ class ConstantReadAccess extends ConstantAccess {
|
||||
*/
|
||||
class ConstantWriteAccess extends ConstantAccess {
|
||||
ConstantWriteAccess() {
|
||||
explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace
|
||||
explicitAssignmentNode(toGenerated(this), _) or
|
||||
this instanceof TNamespace or
|
||||
this instanceof TConstantWriteAccessSynth
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstantWriteAccess" }
|
||||
|
||||
@@ -61,7 +61,7 @@ class ArgumentList extends Expr, TArgumentList {
|
||||
|
||||
private class LhsExpr_ =
|
||||
TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or
|
||||
TDestructuredLhsExpr;
|
||||
TDestructuredLhsExpr or TConstantWriteAccessSynth;
|
||||
|
||||
/**
|
||||
* A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of
|
||||
|
||||
@@ -116,6 +116,9 @@ private module Cached {
|
||||
TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) {
|
||||
mkSynthChild(ConstantReadAccessKind(value), parent, i)
|
||||
} or
|
||||
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
|
||||
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
|
||||
} or
|
||||
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
|
||||
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
|
||||
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
|
||||
@@ -373,12 +376,13 @@ private module Cached {
|
||||
class TAstNodeSynth =
|
||||
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
|
||||
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
|
||||
TConstantReadAccessSynth 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;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Gets the underlying TreeSitter entity for a given AST node. This does not
|
||||
@@ -565,6 +569,8 @@ private module Cached {
|
||||
or
|
||||
result = TConstantReadAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TConstantWriteAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TDivExprSynth(parent, i)
|
||||
or
|
||||
result = TExponentExprSynth(parent, i)
|
||||
@@ -672,7 +678,8 @@ class TMethodCall =
|
||||
class TSuperCall = TTokenSuperCall or TRegularSuperCall;
|
||||
|
||||
class TConstantAccess =
|
||||
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
|
||||
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or
|
||||
TConstantReadAccessSynth or TConstantWriteAccessSynth;
|
||||
|
||||
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ newtype SynthKind =
|
||||
StmtSequenceKind() or
|
||||
SelfKind(SelfVariable v) or
|
||||
SubExprKind() or
|
||||
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) }
|
||||
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or
|
||||
ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) }
|
||||
|
||||
/**
|
||||
* An AST child.
|
||||
@@ -107,6 +108,11 @@ class Synthesis extends TSynthesis {
|
||||
*/
|
||||
predicate constantReadAccess(string name) { none() }
|
||||
|
||||
/**
|
||||
* Holds if a constant write access of `name` is needed.
|
||||
*/
|
||||
predicate constantWriteAccess(string name) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
|
||||
*/
|
||||
@@ -493,6 +499,231 @@ private module AssignOperationDesugar {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment operation where the left-hand side is a constant
|
||||
* without scope expression, such as`FOO` or `::Foo`.
|
||||
*/
|
||||
private class ConstantAssignOperation extends AssignOperation {
|
||||
string name;
|
||||
|
||||
pragma[nomagic]
|
||||
ConstantAssignOperation() {
|
||||
name =
|
||||
any(Ruby::Constant constant | TTokenConstantAccess(constant) = this.getLeftOperand())
|
||||
.getValue()
|
||||
or
|
||||
name =
|
||||
"::" +
|
||||
any(Ruby::Constant constant |
|
||||
TScopeResolutionConstantAccess(any(Ruby::ScopeResolution g | not exists(g.getScope())),
|
||||
constant) = this.getLeftOperand()
|
||||
).getValue()
|
||||
}
|
||||
|
||||
final string getName() { result = name }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate constantAssignOperationSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(ConstantAssignOperation cao |
|
||||
parent = cao and
|
||||
i = -1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(cao, -1) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = childRef(cao.getLeftOperand())
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = SynthChild(getKind(cao))
|
||||
or
|
||||
parent = getSynthChild(assign, 1) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind(cao.getName()))
|
||||
or
|
||||
i = 1 and
|
||||
child = childRef(cao.getRightOperand())
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* FOO += y
|
||||
* ```
|
||||
*
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* FOO = FOO + y
|
||||
* ```
|
||||
*/
|
||||
private class ConstantAssignOperationSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
constantAssignOperationSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) {
|
||||
name = any(ConstantAssignOperation o).getName()
|
||||
}
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(ConstantAssignOperation cao, BinaryOperation bo |
|
||||
bo = cao.getDesugared().(AssignExpr).getRightOperand()
|
||||
|
|
||||
n = bo and
|
||||
l = getAssignOperationLocation(cao)
|
||||
or
|
||||
n = bo.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment operation where the left-hand side is a constant
|
||||
* with scope expression, such as `expr::FOO`.
|
||||
*/
|
||||
private class ScopeResolutionAssignOperation extends AssignOperation {
|
||||
string name;
|
||||
Expr scope;
|
||||
|
||||
pragma[nomagic]
|
||||
ScopeResolutionAssignOperation() {
|
||||
exists(Ruby::Constant constant, Ruby::ScopeResolution g |
|
||||
TScopeResolutionConstantAccess(g, constant) = this.getLeftOperand() and
|
||||
name = constant.getValue() and
|
||||
toGenerated(scope) = g.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
final string getName() { result = name }
|
||||
|
||||
final Expr getScopeExpr() { result = scope }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate scopeResolutionAssignOperationSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(ScopeResolutionAssignOperation cao |
|
||||
parent = cao and
|
||||
i = -1 and
|
||||
child = SynthChild(StmtSequenceKind())
|
||||
or
|
||||
exists(AstNode stmts | stmts = TStmtSequenceSynth(cao, -1) |
|
||||
parent = stmts and
|
||||
i = 0 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(stmts, 0) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = childRef(cao.getScopeExpr())
|
||||
)
|
||||
or
|
||||
parent = stmts and
|
||||
i = 1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(stmts, 1) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantWriteAccessKind(cao.getName()))
|
||||
or
|
||||
exists(AstNode cwa | cwa = TConstantWriteAccessSynth(assign, 0, cao.getName()) |
|
||||
parent = cwa and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
)
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = SynthChild(getKind(cao))
|
||||
or
|
||||
exists(AstNode op | op = getSynthChild(assign, 1) |
|
||||
parent = op and
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind(cao.getName()))
|
||||
or
|
||||
exists(AstNode cra | cra = TConstantReadAccessSynth(op, 0, cao.getName()) |
|
||||
parent = cra and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
)
|
||||
or
|
||||
parent = op and
|
||||
i = 1 and
|
||||
child = childRef(cao.getRightOperand())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* expr::FOO += y
|
||||
* ```
|
||||
*
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* __synth__0 = expr
|
||||
* __synth__0::FOO = _synth__0::FOO + y
|
||||
* ```
|
||||
*/
|
||||
private class ScopeResolutionAssignOperationSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
scopeResolutionAssignOperationSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) {
|
||||
name = any(ScopeResolutionAssignOperation o).getName()
|
||||
}
|
||||
|
||||
final override predicate localVariable(AstNode n, int i) {
|
||||
n instanceof ScopeResolutionAssignOperation and
|
||||
i = 0
|
||||
}
|
||||
|
||||
final override predicate constantWriteAccess(string name) { this.constantReadAccess(name) }
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(ScopeResolutionAssignOperation cao, StmtSequence stmts | stmts = cao.getDesugared() |
|
||||
n = stmts.getStmt(0) and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
or
|
||||
exists(AssignExpr assign | assign = stmts.getStmt(1) |
|
||||
n = assign and hasLocation(cao, l)
|
||||
or
|
||||
n = assign.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
or
|
||||
n = assign.getLeftOperand().(ConstantAccess).getScopeExpr() and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
or
|
||||
exists(BinaryOperation bo | bo = assign.getRightOperand() |
|
||||
n = bo and
|
||||
l = getAssignOperationLocation(cao)
|
||||
or
|
||||
n = bo.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
or
|
||||
n = bo.getLeftOperand().(ConstantAccess).getScopeExpr() and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An assignment operation where the left-hand side is a method call. */
|
||||
private class SetterAssignOperation extends AssignOperation {
|
||||
private MethodCall mc;
|
||||
|
||||
@@ -890,7 +890,12 @@ module Trees {
|
||||
private class ConstantAccessTree extends PostOrderTree, ConstantAccess {
|
||||
ConstantAccessTree() {
|
||||
not this instanceof ClassDeclaration and
|
||||
not this instanceof ModuleDeclaration
|
||||
not this instanceof ModuleDeclaration and
|
||||
// constant accesses with scope expression in compound assignments are desugared
|
||||
not (
|
||||
this = any(AssignOperation op).getLeftOperand() and
|
||||
exists(this.getScopeExpr())
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() }
|
||||
|
||||
Reference in New Issue
Block a user