mirror of
https://github.com/github/codeql.git
synced 2026-05-02 12:15:17 +02:00
Ruby: desugar safe navigation calls
This commit is contained in:
@@ -111,10 +111,10 @@ class IfExpr extends ConditionalExpr, TIfExpr {
|
||||
}
|
||||
}
|
||||
|
||||
private class If extends IfExpr, TIf {
|
||||
private class IfReal extends IfExpr, TIfReal {
|
||||
private Ruby::If g;
|
||||
|
||||
If() { this = TIf(g) }
|
||||
IfReal() { this = TIfReal(g) }
|
||||
|
||||
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
|
||||
|
||||
@@ -125,6 +125,18 @@ private class If extends IfExpr, TIf {
|
||||
final override string toString() { result = "if ..." }
|
||||
}
|
||||
|
||||
private class IfSynth extends IfExpr, TIfSynth {
|
||||
IfSynth() { this = TIfSynth(_, _) }
|
||||
|
||||
final override Expr getCondition() { synthChild(this, 0, result) }
|
||||
|
||||
final override Stmt getThen() { synthChild(this, 1, result) }
|
||||
|
||||
final override Stmt getElse() { synthChild(this, 2, result) }
|
||||
|
||||
final override string toString() { result = "if ..." }
|
||||
}
|
||||
|
||||
private class Elsif extends IfExpr, TElsif {
|
||||
private Ruby::Elsif g;
|
||||
|
||||
|
||||
@@ -162,7 +162,8 @@ private module Cached {
|
||||
} or
|
||||
THereDoc(Ruby::HeredocBeginning g) or
|
||||
TIdentifierMethodCall(Ruby::Identifier g) { isIdentifierMethodCall(g) } or
|
||||
TIf(Ruby::If g) or
|
||||
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
|
||||
TInstanceVariableAccessReal(Ruby::InstanceVariable g, AST::InstanceVariable v) {
|
||||
@@ -214,7 +215,8 @@ private module Cached {
|
||||
TMulExprSynth(AST::AstNode parent, int i) { mkSynthChild(MulExprKind(), parent, i) } or
|
||||
TNEExpr(Ruby::Binary g) { g instanceof @ruby_binary_bangequal } or
|
||||
TNextStmt(Ruby::Next g) or
|
||||
TNilLiteral(Ruby::Nil g) or
|
||||
TNilLiteralReal(Ruby::Nil g) or
|
||||
TNilLiteralSynth(AST::AstNode parent, int i) { mkSynthChild(NilLiteralKind(), parent, i) } or
|
||||
TNoRegExpMatchExpr(Ruby::Binary g) { g instanceof @ruby_binary_bangtilde } or
|
||||
TNotExpr(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or
|
||||
TOptionalParameter(Ruby::OptionalParameter g) or
|
||||
@@ -347,35 +349,36 @@ private module Cached {
|
||||
TFalseLiteral or TFile or TFindPattern or TFloatLiteral or TForExpr or TForwardParameter or
|
||||
TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or
|
||||
THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExpr or
|
||||
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or TIf or
|
||||
TIfModifierExpr or TInClause 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 TModuleDeclaration or TModuloExprReal or TMulExprReal or
|
||||
TNEExpr or TNextStmt or TNilLiteral or TNoRegExpMatchExpr or TNotExpr or
|
||||
TOptionalParameter or TPair or TParenthesizedExpr or TParenthesizedPattern or
|
||||
TRShiftExprReal or TRangeLiteralReal or TRationalLiteral or TRedoStmt or TRegExpLiteral or
|
||||
TRegExpMatchExpr or TRegularArrayLiteral or TRegularMethodCall or TRegularStringLiteral or
|
||||
TRegularSuperCall or TRescueClause or TRescueModifierExpr or TRetryStmt or TReturnStmt or
|
||||
TScopeResolutionConstantAccess or TSelfReal or TSimpleParameterReal or
|
||||
TSimpleSymbolLiteral or TSingletonClass or TSingletonMethod or TSpaceshipExpr or
|
||||
TSplatExprReal or TSplatParameter or TStringArrayLiteral or TStringConcatenation or
|
||||
TStringEscapeSequenceComponent or TStringInterpolationComponent or TStringTextComponent or
|
||||
TSubExprReal or TSubshellLiteral or TSymbolArrayLiteral or TTernaryIfExpr or TThen or
|
||||
TTokenConstantAccess or TTokenMethodName or TTokenSuperCall or TToplevel or TTrueLiteral or
|
||||
TUnaryMinusExpr or TUnaryPlusExpr or TUndefStmt or TUnlessExpr or TUnlessModifierExpr or
|
||||
TUntilExpr or TUntilModifierExpr or TReferencePattern or TWhenClause or TWhileExpr or
|
||||
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or
|
||||
TIfReal or TIfModifierExpr or TInClause 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 TModuleDeclaration or
|
||||
TModuloExprReal or TMulExprReal or TNEExpr or TNextStmt or TNilLiteralReal or
|
||||
TNoRegExpMatchExpr or TNotExpr or TOptionalParameter or TPair or TParenthesizedExpr or
|
||||
TParenthesizedPattern or TRShiftExprReal or TRangeLiteralReal or TRationalLiteral or
|
||||
TRedoStmt or TRegExpLiteral or TRegExpMatchExpr or TRegularArrayLiteral or
|
||||
TRegularMethodCall or TRegularStringLiteral or TRegularSuperCall or TRescueClause or
|
||||
TRescueModifierExpr or TRetryStmt or TReturnStmt or TScopeResolutionConstantAccess or
|
||||
TSelfReal or TSimpleParameterReal or TSimpleSymbolLiteral or TSingletonClass or
|
||||
TSingletonMethod or TSpaceshipExpr or TSplatExprReal or TSplatParameter or
|
||||
TStringArrayLiteral or TStringConcatenation or TStringEscapeSequenceComponent or
|
||||
TStringInterpolationComponent or TStringTextComponent or TSubExprReal or TSubshellLiteral or
|
||||
TSymbolArrayLiteral or TTernaryIfExpr or TThen or TTokenConstantAccess or
|
||||
TTokenMethodName or TTokenSuperCall or TToplevel or TTrueLiteral or TUnaryMinusExpr or
|
||||
TUnaryPlusExpr or TUndefStmt or TUnlessExpr or TUnlessModifierExpr or TUntilExpr or
|
||||
TUntilModifierExpr or TReferencePattern or TWhenClause or TWhileExpr or
|
||||
TWhileModifierExpr or TYieldCall;
|
||||
|
||||
class TAstNodeSynth =
|
||||
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
|
||||
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
|
||||
TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or
|
||||
TGlobalVariableAccessSynth or TInstanceVariableAccessSynth or TIntegerLiteralSynth or
|
||||
TLShiftExprSynth or TLocalVariableAccessSynth or TLogicalAndExprSynth or
|
||||
TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or TMulExprSynth or
|
||||
TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
|
||||
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
|
||||
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
|
||||
@@ -457,7 +460,7 @@ private module Cached {
|
||||
n = THereDoc(result) or
|
||||
n = TIdentifierMethodCall(result) or
|
||||
n = TIfModifierExpr(result) or
|
||||
n = TIf(result) or
|
||||
n = TIfReal(result) or
|
||||
n = TInClause(result) or
|
||||
n = TInstanceVariableAccessReal(result, _) or
|
||||
n = TIntegerLiteralReal(result) or
|
||||
@@ -477,7 +480,7 @@ private module Cached {
|
||||
n = TMulExprReal(result) or
|
||||
n = TNEExpr(result) or
|
||||
n = TNextStmt(result) or
|
||||
n = TNilLiteral(result) or
|
||||
n = TNilLiteralReal(result) or
|
||||
n = TNoRegExpMatchExpr(result) or
|
||||
n = TNotExpr(result) or
|
||||
n = TOptionalParameter(result) or
|
||||
@@ -568,6 +571,8 @@ private module Cached {
|
||||
or
|
||||
result = TGlobalVariableAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TIfSynth(parent, i)
|
||||
or
|
||||
result = TInstanceVariableAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TIntegerLiteralSynth(parent, i, _)
|
||||
@@ -586,6 +591,8 @@ private module Cached {
|
||||
or
|
||||
result = TMulExprSynth(parent, i)
|
||||
or
|
||||
result = TNilLiteralSynth(parent, i)
|
||||
or
|
||||
result = TRangeLiteralSynth(parent, i, _)
|
||||
or
|
||||
result = TRShiftExprSynth(parent, i)
|
||||
@@ -672,6 +679,8 @@ class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
|
||||
class TConditionalExpr =
|
||||
TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr;
|
||||
|
||||
class TIf = TIfReal or TIfSynth;
|
||||
|
||||
class TIfExpr = TIf or TElsif;
|
||||
|
||||
class TConditionalLoop = TWhileExpr or TUntilExpr or TWhileModifierExpr or TUntilModifierExpr;
|
||||
@@ -695,6 +704,8 @@ class TStmtSequence =
|
||||
|
||||
class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod;
|
||||
|
||||
class TNilLiteral = TNilLiteralReal or TNilLiteralSynth;
|
||||
|
||||
class TLiteral =
|
||||
TEncoding or TFile or TLine or TNumericLiteral or TNilLiteral or TBooleanLiteral or
|
||||
TStringlikeLiteral or TCharacterLiteral or TArrayLiteral or THashLiteral or TRangeLiteral or
|
||||
|
||||
@@ -111,12 +111,18 @@ class ComplexLiteralImpl extends Expr, TComplexLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
class NilLiteralImpl extends Expr, TNilLiteral {
|
||||
abstract class NilLiteralImpl extends Expr, TNilLiteral {
|
||||
final override string toString() { result = "nil" }
|
||||
}
|
||||
|
||||
class NilLiteralReal extends NilLiteralImpl, TNilLiteralReal {
|
||||
private Ruby::Nil g;
|
||||
|
||||
NilLiteralImpl() { this = TNilLiteral(g) }
|
||||
NilLiteralReal() { this = TNilLiteralReal(g) }
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
class NilLiteralSynth extends NilLiteralImpl, TNilLiteralSynth {
|
||||
NilLiteralSynth() { this = TNilLiteralSynth(_, _) }
|
||||
}
|
||||
|
||||
abstract class BooleanLiteralImpl extends Expr, TBooleanLiteral {
|
||||
|
||||
@@ -21,6 +21,7 @@ newtype SynthKind =
|
||||
DivExprKind() or
|
||||
ExponentExprKind() or
|
||||
GlobalVariableAccessKind(GlobalVariable v) or
|
||||
IfKind() or
|
||||
InstanceVariableAccessKind(InstanceVariable v) or
|
||||
IntegerLiteralKind(int i) { i in [-1000 .. 1000] } or
|
||||
LShiftExprKind() or
|
||||
@@ -33,6 +34,7 @@ newtype SynthKind =
|
||||
} or
|
||||
ModuloExprKind() or
|
||||
MulExprKind() or
|
||||
NilLiteralKind() or
|
||||
RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or
|
||||
RShiftExprKind() or
|
||||
SimpleParameterKind() or
|
||||
@@ -1083,3 +1085,123 @@ private module AnonymousBlockParameterSynth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private module SafeNavigationCallDesugar {
|
||||
/**
|
||||
* ```rb
|
||||
* receiver&.method(args) { ... }
|
||||
* ```
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* __synth__0 = receiver
|
||||
* if nil == __synth__0 then nil else __synth__0.method(args) {...} end
|
||||
* ```
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate safeNavigationCallSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(RegularMethodCall call, LocalVariableAccessSynthKind local |
|
||||
call.isSafeNavigationImpl() and
|
||||
local = LocalVariableAccessSynthKind(TLocalVariableSynth(call.getReceiverImpl(), 0))
|
||||
|
|
||||
parent = call and
|
||||
i = -1 and
|
||||
child = SynthChild(StmtSequenceKind())
|
||||
or
|
||||
exists(TStmtSequenceSynth seq | seq = TStmtSequenceSynth(call, -1) |
|
||||
parent = seq and
|
||||
(
|
||||
child = SynthChild(AssignExprKind()) and i = 0
|
||||
or
|
||||
child = SynthChild(IfKind()) and i = 1
|
||||
)
|
||||
or
|
||||
parent = TAssignExprSynth(seq, 0) and
|
||||
(
|
||||
child = SynthChild(local) and
|
||||
i = 0
|
||||
or
|
||||
child = childRef(call.getReceiverImpl()) and i = 1
|
||||
)
|
||||
or
|
||||
exists(TIfSynth ifExpr | ifExpr = TIfSynth(seq, 1) |
|
||||
parent = ifExpr and
|
||||
(
|
||||
child = SynthChild(MethodCallKind("==", false, 2)) and
|
||||
i = 0
|
||||
or
|
||||
child = SynthChild(NilLiteralKind()) and i = 1
|
||||
or
|
||||
child =
|
||||
SynthChild(MethodCallKind(call.getMethodNameImpl(), false,
|
||||
call.getNumberOfArgumentsImpl())) and
|
||||
i = 2
|
||||
)
|
||||
or
|
||||
parent = TMethodCallSynth(ifExpr, 0, _, _, _) and
|
||||
(
|
||||
child = SynthChild(NilLiteralKind()) and i = 0
|
||||
or
|
||||
child = SynthChild(local) and
|
||||
i = 1
|
||||
)
|
||||
or
|
||||
parent = TMethodCallSynth(ifExpr, 2, _, _, _) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(local)
|
||||
or
|
||||
child = childRef(call.getArgumentImpl(i - 1))
|
||||
or
|
||||
child = childRef(call.getBlockImpl()) and i = -2
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class SafeNavigationCallSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
safeNavigationCallSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
exists(RegularMethodCall call |
|
||||
call.isSafeNavigationImpl() and
|
||||
name = call.getMethodNameImpl() and
|
||||
setter = false and
|
||||
arity = call.getNumberOfArgumentsImpl()
|
||||
)
|
||||
or
|
||||
name = "==" and setter = false and arity = 2
|
||||
}
|
||||
|
||||
final override predicate localVariable(AstNode n, int i) {
|
||||
i = 0 and n = any(RegularMethodCall c | c.isSafeNavigationImpl()).getReceiverImpl()
|
||||
}
|
||||
|
||||
override predicate location(AstNode n, Location l) {
|
||||
exists(RegularMethodCall call, StmtSequence seq |
|
||||
call.isSafeNavigationImpl() and seq = call.getDesugared()
|
||||
|
|
||||
n = seq.getStmt(0) and
|
||||
hasLocation(call.getReceiverImpl(), l)
|
||||
or
|
||||
n = seq.getStmt(1) and
|
||||
l = toGenerated(call).(Ruby::Call).getOperator().getLocation()
|
||||
or
|
||||
n = seq.getStmt(1).(IfExpr).getCondition().(MethodCall).getArgument(0) and
|
||||
hasLocation(call.getReceiverImpl(), l)
|
||||
or
|
||||
n = seq.getStmt(1).(IfExpr).getThen() and
|
||||
hasLocation(call.getReceiverImpl(), l)
|
||||
or
|
||||
n = seq.getStmt(1).(IfExpr).getElse() and
|
||||
hasLocation(call, l)
|
||||
or
|
||||
n = seq.getStmt(1).(IfExpr).getElse().(MethodCall).getReceiver() and
|
||||
hasLocation(call.getReceiverImpl(), l)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +279,19 @@ calls/calls.rb:
|
||||
# 340| getArgument: [IntegerLiteral] 4
|
||||
# 340| getArgument: [IntegerLiteral] 5
|
||||
# 340| getArgument: [IntegerLiteral] 6
|
||||
# 362| [MethodCall] call to empty?
|
||||
# 362| getDesugared: [StmtSequence] ...
|
||||
# 362| getStmt: [AssignExpr] ... = ...
|
||||
# 362| getAnOperand/getRightOperand: [MethodCall] call to list
|
||||
# 362| getReceiver: [SelfVariableAccess] self
|
||||
# 362| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 362| getStmt: [IfExpr] if ...
|
||||
# 362| getBranch/getElse: [MethodCall] call to empty?
|
||||
# 362| getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 362| getBranch/getThen: [NilLiteral] nil
|
||||
# 362| getCondition: [MethodCall] call to ==
|
||||
# 362| getArgument: [LocalVariableAccess] __synth__0__1
|
||||
# 362| getReceiver: [NilLiteral] nil
|
||||
control/cases.rb:
|
||||
# 90| [ArrayLiteral] %w(...)
|
||||
# 90| getDesugared: [MethodCall] call to []
|
||||
|
||||
@@ -74,6 +74,8 @@ exprValue
|
||||
| calls/calls.rb:346:8:346:9 | 42 | 42 | int |
|
||||
| calls/calls.rb:347:5:347:5 | :X | :X | symbol |
|
||||
| calls/calls.rb:350:5:350:5 | 1 | 1 | int |
|
||||
| calls/calls.rb:362:1:362:4 | nil | nil | nil |
|
||||
| calls/calls.rb:362:5:362:6 | nil | nil | nil |
|
||||
| constants/constants.rb:3:19:3:27 | "const_a" | const_a | string |
|
||||
| constants/constants.rb:6:15:6:23 | "const_b" | const_b | string |
|
||||
| constants/constants.rb:17:12:17:18 | "Hello" | Hello | string |
|
||||
@@ -963,6 +965,8 @@ exprCfgNodeValue
|
||||
| calls/calls.rb:346:8:346:9 | 42 | 42 | int |
|
||||
| calls/calls.rb:347:5:347:5 | :X | :X | symbol |
|
||||
| calls/calls.rb:350:5:350:5 | 1 | 1 | int |
|
||||
| calls/calls.rb:362:1:362:4 | nil | nil | nil |
|
||||
| calls/calls.rb:362:5:362:6 | nil | nil | nil |
|
||||
| constants/constants.rb:3:19:3:27 | "const_a" | const_a | string |
|
||||
| constants/constants.rb:6:15:6:23 | "const_b" | const_b | string |
|
||||
| constants/constants.rb:17:12:17:18 | "Hello" | Hello | string |
|
||||
|
||||
@@ -114,6 +114,7 @@ callsWithArguments
|
||||
| calls.rb:346:1:346:10 | call to foo | foo | 0 | calls.rb:346:5:346:9 | Pair |
|
||||
| calls.rb:347:1:347:7 | call to foo | foo | 0 | calls.rb:347:5:347:6 | Pair |
|
||||
| calls.rb:352:13:352:17 | call to foo | foo | 0 | calls.rb:352:17:352:17 | x |
|
||||
| calls.rb:362:5:362:6 | call to == | == | 0 | calls.rb:362:1:362:4 | __synth__0__1 |
|
||||
callsWithReceiver
|
||||
| calls.rb:2:1:2:5 | call to foo | calls.rb:2:1:2:5 | self |
|
||||
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo |
|
||||
@@ -375,7 +376,9 @@ callsWithReceiver
|
||||
| calls.rb:361:1:361:4 | call to list | calls.rb:361:1:361:4 | self |
|
||||
| calls.rb:361:1:361:11 | call to empty? | calls.rb:361:1:361:4 | call to list |
|
||||
| calls.rb:362:1:362:4 | call to list | calls.rb:362:1:362:4 | self |
|
||||
| calls.rb:362:1:362:12 | call to empty? | calls.rb:362:1:362:4 | __synth__0__1 |
|
||||
| calls.rb:362:1:362:12 | call to empty? | calls.rb:362:1:362:4 | call to list |
|
||||
| calls.rb:362:5:362:6 | call to == | calls.rb:362:5:362:6 | nil |
|
||||
| calls.rb:363:1:363:4 | call to list | calls.rb:363:1:363:4 | self |
|
||||
| calls.rb:363:1:363:12 | call to empty? | calls.rb:363:1:363:4 | call to list |
|
||||
callsWithBlock
|
||||
|
||||
Reference in New Issue
Block a user