mirror of
https://github.com/github/codeql.git
synced 2026-07-20 18:58:36 +02:00
Merge branch 'main' into amammad-python-WebAppsConstatntSecretKeys
This commit is contained in:
@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
{
|
||||
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
|
||||
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
|
||||
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
|
||||
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
|
||||
|
||||
@@ -1520,6 +1520,25 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand.getDef() = instr`, but there exists a `StoreInstruction` that
|
||||
* writes to an address that is equivalent to the value computed by `instr` in
|
||||
* between `instr` and `operand`, and therefore there should not be flow from `*instr`
|
||||
* to `*operand`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate isStoredToBetween(Instruction instr, Operand operand) {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) and
|
||||
exists(StoreInstruction store, IRBlock block, int storeIndex, int instrIndex, int operandIndex |
|
||||
store.getDestinationAddress() = instr and
|
||||
block.getInstruction(storeIndex) = store and
|
||||
block.getInstruction(instrIndex) = instr and
|
||||
block.getInstruction(operandIndex) = operand.getUse() and
|
||||
instrIndex < storeIndex and
|
||||
storeIndex < operandIndex
|
||||
)
|
||||
}
|
||||
|
||||
private predicate indirectionInstructionFlow(
|
||||
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
|
||||
) {
|
||||
@@ -1529,7 +1548,8 @@ private module Cached {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand))
|
||||
|
|
||||
hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex))
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex)) and
|
||||
not isStoredToBetween(instr, operand)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
@@ -864,7 +865,7 @@ private module Cached {
|
||||
* to a specific address.
|
||||
*/
|
||||
private predicate isCertainAddress(Operand operand) {
|
||||
operand.getDef() instanceof VariableAddressInstruction
|
||||
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
|
||||
or
|
||||
operand.getType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
@@ -574,16 +574,6 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
@@ -597,27 +587,6 @@ module RangeStage<
|
||||
delta = D::fromInt(0) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = D::fromInt(-1)
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = D::fromInt(0)
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = D::fromInt(1)
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = D::fromInt(0)
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = D::fromInt(-1) and
|
||||
@@ -1137,6 +1106,23 @@ module RangeStage<
|
||||
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight |
|
||||
boundedSubOperandLeft(e, upper, b, dLeft, fbeLeft, origdelta, reason) and
|
||||
boundedSubOperandRight(e, upper, dRight, fbeRight) and
|
||||
// when `upper` is `true` we have:
|
||||
// left <= b + dLeft
|
||||
// right >= 0 + dRight
|
||||
// left - right <= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
// and when `upper` is `false` we have:
|
||||
// left >= b + dLeft
|
||||
// right <= 0 + dRight
|
||||
// left - right >= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
delta = D::fromFloat(D::toFloat(dLeft) - D::toFloat(dRight)) and
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SemRemExpr rem, D::Delta d_max, D::Delta d1, D::Delta d2, boolean fbe1, boolean fbe2,
|
||||
D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
|
||||
@@ -1201,6 +1187,37 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `left <= b + delta` if `upper` is `true`
|
||||
* and `left >= b + delta` is `upper` is `false`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandLeft(
|
||||
SemSubExpr sub, boolean upper, SemBound b, D::Delta delta, boolean fromBackEdge,
|
||||
D::Delta origdelta, SemReason reason
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `right <= 0 + delta` if `upper` is `false`
|
||||
* and `right >= 0 + delta` is `upper` is `true`.
|
||||
*
|
||||
* Note that the boolean value of `upper` is flipped compared to many other predicates in
|
||||
* this file. This ensures a clean join at the call-site.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandRight(
|
||||
SemSubExpr sub, boolean upper, D::Delta delta, boolean fromBackEdge
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getRightOperand(), any(SemZeroBound zb), delta, upper.booleanNot(), fromBackEdge, _,
|
||||
_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate boundedRemExpr(
|
||||
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,
|
||||
|
||||
@@ -96,7 +96,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
* but because there's a strict comparison that compares `n` against the size of the allocation this
|
||||
* snippet is fine.
|
||||
*/
|
||||
module SizeBarrier {
|
||||
private module SizeBarrier {
|
||||
private module SizeBarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for the second
|
||||
@@ -104,35 +104,60 @@ module SizeBarrier {
|
||||
hasSize(_, source, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `testIsTrue`.
|
||||
*/
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
|
||||
// in a guard such as `left < right + k`.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `large` expression
|
||||
// in a guard such as `small <= large + k`.
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k + 1, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
}
|
||||
|
||||
private import DataFlow::Global<SizeBarrierConfig>
|
||||
module SizeBarrierFlow = DataFlow::Global<SizeBarrierConfig>;
|
||||
|
||||
private int getAFlowStateForNode(DataFlow::Node node) {
|
||||
private int getASizeAddend(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
flow(source, node) and
|
||||
SizeBarrierFlow::flow(source, node) and
|
||||
hasSize(_, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `edge`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
IRGuardCondition g, Operand small, DataFlow::Node large, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getAFlowStateForNode(nRight) and
|
||||
k <= state
|
||||
SizeBarrierFlow::flowTo(large) and
|
||||
SizeBarrierConfig::isSink(DataFlow::operandNode(small), large, g, k, edge)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction `instr` that is guarded by a check such as `instr <= small + delta` where
|
||||
* `small <= _ + k` and `small` is the "small side" of of a relational comparison that checks
|
||||
* whether `small <= size` where `size` is the size of an allocation.
|
||||
*/
|
||||
Instruction getABarrierInstruction0(int delta, int k) {
|
||||
exists(
|
||||
IRGuardCondition g, ValueNumber value, Operand small, boolean edge, DataFlow::Node large
|
||||
|
|
||||
// We know:
|
||||
// 1. result <= value + delta (by `bounded`)
|
||||
// 2. value <= large + k (by `operandGuardChecks`).
|
||||
// So:
|
||||
// result <= value + delta (by 1.)
|
||||
// <= large + k + delta (by 2.)
|
||||
small = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](small), large,
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
k < getASizeAddend(large)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,13 +165,14 @@ module SizeBarrier {
|
||||
* Gets an instruction that is guarded by a guard condition which ensures that
|
||||
* the value of the instruction is upper-bounded by size of some allocation.
|
||||
*/
|
||||
bindingset[state]
|
||||
pragma[inline_late]
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
|
||||
pragma[only_bind_into](state), pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
exists(int delta, int k |
|
||||
state > k + delta and
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
result = getABarrierInstruction0(delta, k)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -155,14 +181,16 @@ module SizeBarrier {
|
||||
* the value of the node is upper-bounded by size of some allocation.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode(int state) {
|
||||
result.asOperand() = getABarrierInstruction(state).getAUse()
|
||||
exists(DataFlow::Node source, int delta, int k |
|
||||
SizeBarrierFlow::flow(source, result) and
|
||||
hasSize(_, source, state) and
|
||||
result.asInstruction() = SizeBarrier::getABarrierInstruction0(delta, k) and
|
||||
state > k + delta
|
||||
// so now we have:
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
|
||||
* `getABarrierNode` for the definition of what it means to be guarded).
|
||||
*/
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
}
|
||||
|
||||
private module InterestingPointerAddInstruction {
|
||||
|
||||
@@ -66,11 +66,14 @@
|
||||
* module. Since the node we are tracking is not necessarily _equal_ to the pointer-arithmetic instruction, but rather satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`, we need to account for the delta when checking if a guard is sufficiently
|
||||
* strong to infer that a future dereference is safe. To do this, we check that the guard guarantees that a node `n` satisfies
|
||||
* `n < node + k` where `node` is a node we know is equal to the value of the dereference source (i.e., it satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`) and `k <= deltaDerefSourceAndPai`. Combining this we have
|
||||
* `n < node + k <= node + deltaDerefSourceAndPai <= pai + 2*deltaDerefSourceAndPai` (TODO: Oops. This math doesn't quite work out.
|
||||
* I think this is because we need to redefine the `BarrierConfig` to start flow at the pointer-arithmetic instruction instead of
|
||||
* at the dereference source. When combined with TODO above it's easy to show that this guard ensures that the dereference is safe).
|
||||
* `n < node + k` where `node` is a node such that `node <= pai`. Thus, we know that any node `m` such that `m <= n + delta` where
|
||||
* `delta + k <= 0` will be safe because:
|
||||
* ```
|
||||
* m <= n + delta
|
||||
* < node + k + delta
|
||||
* <= pai + k + delta
|
||||
* <= pai
|
||||
* ```
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
@@ -82,16 +85,19 @@ private import RangeAnalysisUtil
|
||||
|
||||
private module InvalidPointerToDerefBarrier {
|
||||
private module BarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
|
||||
invalidPointerToDerefSource(_, _, source, _)
|
||||
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
|
||||
invalidPointerToDerefSource(_, pai, _, _) and
|
||||
// source <= pai
|
||||
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
@@ -99,59 +105,82 @@ private module InvalidPointerToDerefBarrier {
|
||||
|
||||
private module BarrierFlow = DataFlow::Global<BarrierConfig>;
|
||||
|
||||
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
BarrierFlow::flow(source, node) and
|
||||
invalidPointerToDerefSource(_, _, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` ensures that `small < large + k` if `g` evaluates to `edge`.
|
||||
*
|
||||
* Additionally, it also holds that `large <= pai`. Thus, when `g` evaluates to `edge`
|
||||
* it holds that `small < pai + k`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
PointerArithmeticInstruction pai, IRGuardCondition g, Operand small, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getInvalidPointerToDerefSourceDelta(nRight) and
|
||||
k <= state
|
||||
exists(DataFlow::Node source, DataFlow::Node nSmall, DataFlow::Node nLarge |
|
||||
nSmall.asOperand() = small and
|
||||
BarrierConfig::isSource(source, pai) and
|
||||
BarrierFlow::flow(source, nLarge) and
|
||||
BarrierConfig::isSink(nSmall, nLarge, g, k, edge)
|
||||
)
|
||||
}
|
||||
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
/**
|
||||
* Gets an instruction `instr` such that `instr < pai`.
|
||||
*/
|
||||
Instruction getABarrierInstruction(PointerArithmeticInstruction pai) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge, int delta, int k |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state,
|
||||
pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
// value < pai + k
|
||||
operandGuardChecks(pai, pragma[only_bind_into](g), pragma[only_bind_into](use),
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
// result <= value + delta
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
delta + k <= 0
|
||||
// combining the above we have: result < pai + k + delta <= pai
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() }
|
||||
DataFlow::Node getABarrierNode(PointerArithmeticInstruction pai) {
|
||||
result.asOperand() = getABarrierInstruction(pai).getAUse()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
/**
|
||||
* Gets an address operand whose definition `instr` satisfies `instr < pai`.
|
||||
*/
|
||||
AddressOperand getABarrierAddressOperand(PointerArithmeticInstruction pai) {
|
||||
result.getDef() = getABarrierInstruction(pai)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a pointer-arithmetic operation found
|
||||
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
|
||||
*/
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, _, source, _) }
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends PointerArithmeticInstruction {
|
||||
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState pai) {
|
||||
invalidPointerToDerefSource(_, pai, source, _)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState pai) { none() }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
|
||||
or
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState pai) {
|
||||
// `node = getABarrierNode(pai)` ensures that node < pai, so this node is safe to dereference.
|
||||
// Note that this is the only place where the `FlowState` is used in this configuration.
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode(pai)
|
||||
}
|
||||
}
|
||||
|
||||
private import DataFlow::Global<InvalidPointerToDerefConfig>
|
||||
private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
|
||||
|
||||
/**
|
||||
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
|
||||
@@ -165,19 +194,14 @@ private predicate invalidPointerToDerefSource(
|
||||
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
|
||||
int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(int rhsSizeDelta |
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0 and
|
||||
// TODO: This condition will go away once #13725 is merged, and then we can make `SizeBarrier`
|
||||
// private to `AllocationToInvalidPointer.qll`.
|
||||
not derefSource.getBasicBlock() =
|
||||
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
|
||||
)
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
|
||||
// derefSource <= pai + deltaDerefSourceAndPai
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,15 +211,14 @@ private predicate invalidPointerToDerefSource(
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isInvalidPointerDerefSink(
|
||||
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
|
||||
DataFlow::Node sink, AddressOperand addr, Instruction i, string operation,
|
||||
int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(AddressOperand addr, Instruction s, IRBlock b |
|
||||
exists(Instruction s |
|
||||
s = sink.asInstruction() and
|
||||
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
|
||||
deltaDerefSinkAndDerefAddress >= 0 and
|
||||
i.getAnOperand() = addr and
|
||||
b = i.getBlock() and
|
||||
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
|
||||
i.getAnOperand() = addr
|
||||
|
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
@@ -221,9 +244,11 @@ private Instruction getASuccessor(Instruction instr) {
|
||||
instr.getBlock().getASuccessor+() = result.getBlock()
|
||||
}
|
||||
|
||||
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
|
||||
private predicate paiForDereferenceSink(
|
||||
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(DataFlow::Node derefSource |
|
||||
invalidPointerToDerefSource(_, pai, derefSource, _) and
|
||||
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink)
|
||||
)
|
||||
}
|
||||
@@ -235,13 +260,15 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
|
||||
*/
|
||||
private predicate derefSinkToOperation(
|
||||
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
|
||||
string description, int deltaDerefSinkAndDerefAddress
|
||||
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(Instruction operationInstr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
|
||||
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
|
||||
exists(Instruction operationInstr, AddressOperand addr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
|
||||
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
operationInstr = getASuccessor(derefSink.asInstruction()) and
|
||||
operation.asInstruction() = operationInstr
|
||||
operation.asInstruction() = operationInstr and
|
||||
not addr = InvalidPointerToDerefBarrier::getABarrierAddressOperand(pai)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,7 +287,8 @@ predicate operationIsOffBy(
|
||||
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
|
||||
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSinkAndDerefAddress) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ private Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
* Holds if `i <= b + delta`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
private predicate boundedImplCand(Instruction i, Instruction b, int delta) {
|
||||
exists(SemBound bound, IRFunction func |
|
||||
semBounded(getSemanticExpr(i), bound, delta, true, _) and
|
||||
b = getABoundIn(bound, func) and
|
||||
@@ -26,6 +26,15 @@ private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta` and `delta` is the smallest integer that satisfies
|
||||
* this condition.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
delta = min(int cand | boundedImplCand(i, b, cand))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta`.
|
||||
*
|
||||
|
||||
@@ -69,12 +69,6 @@ edges
|
||||
| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end |
|
||||
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... |
|
||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 |
|
||||
| test.cpp:351:9:351:11 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | test.cpp:351:9:351:14 | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array |
|
||||
nodes
|
||||
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
|
||||
| test.cpp:34:10:34:12 | buf | semmle.label | buf |
|
||||
@@ -167,11 +161,6 @@ nodes
|
||||
| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
| test.cpp:325:24:325:26 | end | semmle.label | end |
|
||||
| test.cpp:351:9:351:11 | arr | semmle.label | arr |
|
||||
| test.cpp:351:9:351:14 | access to array | semmle.label | access to array |
|
||||
| test.cpp:351:18:351:20 | arr | semmle.label | arr |
|
||||
| test.cpp:351:18:351:25 | access to array | semmle.label | access to array |
|
||||
| test.cpp:351:29:351:31 | arr | semmle.label | arr |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
|
||||
@@ -194,6 +183,3 @@ subpaths
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:330:13:330:24 | Store: ... = ... | write |
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:331:13:331:24 | Store: ... = ... | write |
|
||||
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:333:13:333:24 | Store: ... = ... | write |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
|
||||
|
||||
@@ -348,7 +348,7 @@ int positiveRange(int x) {
|
||||
int arr[128];
|
||||
|
||||
for(int i=127-offset; i>= 0; i--) {
|
||||
arr[i] = arr[i+1] + arr[i+offset]; // GOOD [FALSE POSITIVE]
|
||||
arr[i] = arr[i+1] + arr[i+offset]; // GOOD
|
||||
}
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
@@ -129,7 +129,6 @@ edges
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
|
||||
| test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
|
||||
| test.cpp:355:14:355:27 | new[] | test.cpp:357:24:357:30 | ... + ... |
|
||||
@@ -214,20 +213,21 @@ edges
|
||||
| test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... |
|
||||
| test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... |
|
||||
| test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:656:3:656:6 | ... ++ |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
|
||||
| test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... |
|
||||
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | test.cpp:698:5:698:10 | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | test.cpp:701:15:701:16 | * ... |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:705:18:705:18 | q |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
|
||||
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
|
||||
| test.cpp:711:13:711:26 | new[] | test.cpp:714:11:714:11 | q |
|
||||
| test.cpp:714:11:714:11 | q | test.cpp:705:18:705:18 | q |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:732:16:732:26 | ... + ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
|
||||
nodes
|
||||
| test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc |
|
||||
| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
|
||||
@@ -320,8 +320,6 @@ nodes
|
||||
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:274:5:274:10 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:304:15:304:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:308:5:308:29 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:355:14:355:27 | new[] | semmle.label | new[] |
|
||||
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
|
||||
@@ -371,20 +369,19 @@ nodes
|
||||
| test.cpp:559:5:559:19 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:642:14:642:31 | new[] | semmle.label | new[] |
|
||||
| test.cpp:647:5:647:19 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:652:14:652:27 | new[] | semmle.label | new[] |
|
||||
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
|
||||
| test.cpp:662:3:662:11 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:667:14:667:31 | new[] | semmle.label | new[] |
|
||||
| test.cpp:675:7:675:23 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:695:13:695:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:701:15:701:16 | * ... | semmle.label | * ... |
|
||||
| test.cpp:705:18:705:18 | q | semmle.label | q |
|
||||
| test.cpp:705:18:705:18 | q | semmle.label | q |
|
||||
| test.cpp:706:12:706:13 | * ... | semmle.label | * ... |
|
||||
| test.cpp:711:13:711:26 | new[] | semmle.label | new[] |
|
||||
| test.cpp:714:11:714:11 | q | semmle.label | q |
|
||||
| test.cpp:730:12:730:28 | new[] | semmle.label | new[] |
|
||||
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:733:5:733:12 | ... = ... | semmle.label | ... = ... |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:6:14:6:15 | * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
|
||||
@@ -406,7 +403,6 @@ subpaths
|
||||
| test.cpp:254:9:254:16 | ... = ... | test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:16 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:248:24:248:30 | call to realloc | call to realloc | test.cpp:254:11:254:11 | i | i |
|
||||
| test.cpp:264:13:264:14 | * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
|
||||
| test.cpp:274:5:274:10 | ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
|
||||
| test.cpp:308:5:308:29 | ... = ... | test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:304:15:304:26 | new[] | new[] | test.cpp:308:8:308:10 | ... + ... | ... + ... |
|
||||
| test.cpp:358:14:358:26 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
|
||||
| test.cpp:359:14:359:32 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
|
||||
| test.cpp:384:13:384:16 | * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size |
|
||||
@@ -418,7 +414,6 @@ subpaths
|
||||
| test.cpp:548:5:548:19 | ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos |
|
||||
| test.cpp:559:5:559:19 | ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos |
|
||||
| test.cpp:647:5:647:19 | ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos |
|
||||
| test.cpp:662:3:662:11 | ... = ... | test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:652:14:652:27 | new[] | new[] | test.cpp:653:19:653:22 | size | size |
|
||||
| test.cpp:675:7:675:23 | ... = ... | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:667:14:667:31 | new[] | new[] | test.cpp:675:10:675:18 | ... ++ | ... ++ |
|
||||
| test.cpp:701:15:701:16 | * ... | test.cpp:695:13:695:26 | new[] | test.cpp:701:15:701:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:695:13:695:26 | new[] | new[] | test.cpp:696:19:696:22 | size | size |
|
||||
| test.cpp:706:12:706:13 | * ... | test.cpp:711:13:711:26 | new[] | test.cpp:706:12:706:13 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:711:13:711:26 | new[] | new[] | test.cpp:712:19:712:22 | size | size |
|
||||
| test.cpp:733:5:733:12 | ... = ... | test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:730:12:730:28 | new[] | new[] | test.cpp:732:21:732:25 | ... + ... | ... + ... |
|
||||
|
||||
@@ -305,7 +305,7 @@ void test21() {
|
||||
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
xs[i] = test21_get(i); // GOOD
|
||||
xs[i+1] = test21_get(i+1); // $ alloc=L304 alloc=L304-1 deref=L308 // GOOD [FALSE POSITIVE]
|
||||
xs[i+1] = test21_get(i+1); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ void test32(unsigned size) {
|
||||
xs++;
|
||||
if (xs >= end)
|
||||
return;
|
||||
xs[0] = 0; // $ deref=L656->L662+1 deref=L657->L662+1 GOOD [FALSE POSITIVE]
|
||||
xs[0] = 0; // GOOD
|
||||
}
|
||||
|
||||
void test33(unsigned size, unsigned src_pos)
|
||||
@@ -672,7 +672,7 @@ void test33(unsigned size, unsigned src_pos)
|
||||
while (dst_pos < size - 1) {
|
||||
dst_pos++;
|
||||
if (true)
|
||||
xs[dst_pos++] = 0; // $ alloc=L667+1 deref=L675 // GOOD [FALSE POSITIVE]
|
||||
xs[dst_pos++] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,3 +714,31 @@ void test35(unsigned long size, char* q)
|
||||
deref(q);
|
||||
}
|
||||
}
|
||||
|
||||
void test21_simple(bool b) {
|
||||
int n = 0;
|
||||
if (b) n = 2;
|
||||
|
||||
int* xs = new int[n];
|
||||
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
xs[i+1] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test36(unsigned size, unsigned n) {
|
||||
int* p = new int[size + 2];
|
||||
if(n < size + 1) {
|
||||
int* end = p + (n + 2); // $ alloc=L730+2
|
||||
*end = 0; // $ deref=L733 // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void test37(unsigned long n)
|
||||
{
|
||||
int *p = new int[n];
|
||||
for (unsigned long i = n; i != 0u; i--)
|
||||
{
|
||||
p[n - i] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
@@ -732,7 +732,7 @@ void test_does_not_write_source_to_dereference()
|
||||
{
|
||||
int x;
|
||||
does_not_write_source_to_dereference(&x);
|
||||
sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11
|
||||
sink(x); // $ ast=733:7 ir SPURIOUS: ast=726:11
|
||||
}
|
||||
|
||||
void sometimes_calls_sink_eq(int x, int n) {
|
||||
|
||||
@@ -134,7 +134,7 @@ void pointer_test() {
|
||||
sink(*p3); // $ ast,ir
|
||||
|
||||
*p3 = 0;
|
||||
sink(*p3); // $ SPURIOUS: ast,ir
|
||||
sink(*p3); // $ SPURIOUS: ast
|
||||
}
|
||||
|
||||
// --- return values ---
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
| file://:0:0:0:0 | There was an error during this compilation |
|
||||
| float128.cpp:1:39:1:39 | 128-bit floating-point types are not supported in this configuration |
|
||||
| float128.cpp:2:30:2:30 | 128-bit floating-point types are not supported in this configuration |
|
||||
@@ -1,4 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from Diagnostic d
|
||||
select d
|
||||
@@ -1,5 +1,5 @@
|
||||
typedef _Complex float __attribute__((mode(TC))) _Complex128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
|
||||
typedef float __attribute__((mode(TF))) _Float128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
|
||||
typedef _Complex float __attribute__((mode(TC))) _Complex128;
|
||||
typedef float __attribute__((mode(TF))) _Float128;
|
||||
|
||||
int main() {
|
||||
__float128 f = 1.0f;
|
||||
@@ -25,4 +25,3 @@ __float128 id(__float128 q)
|
||||
{
|
||||
return q;
|
||||
}
|
||||
// semmle-extractor-options: --expect_errors
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | <error-type> |
|
||||
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | <error-type> |
|
||||
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | float __complex__ |
|
||||
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | __float128 |
|
||||
| float128.cpp:13:29:13:54 | __is_floating_point_helper<T> | float128.cpp:10:8:10:17 | false_type |
|
||||
| float128.cpp:14:19:14:51 | __is_floating_point_helper<float> | float128.cpp:11:8:11:16 | true_type |
|
||||
| float128.cpp:15:19:15:52 | __is_floating_point_helper<double> | float128.cpp:11:8:11:16 | true_type |
|
||||
|
||||
@@ -95,3 +95,25 @@ void gotoLoop(bool b1, bool b2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_sub(int x, int y, int n) {
|
||||
if(x > 0 && x < 500) {
|
||||
if(y > 0 && y < 10) {
|
||||
range(x - y); // $ range=<=498 range=>=-8
|
||||
}
|
||||
|
||||
if(n > 0 && n < 100) {
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
range(n - i); // $ range=">=Phi: i-97" range=<=99 range=>=-97
|
||||
range(i - n); // $ range="<=Phi: i-1" range=">=Phi: i-99" range=<=97 range=>=-99
|
||||
}
|
||||
|
||||
for (int i = n; i != 0; i--)
|
||||
{
|
||||
range(n - i); // $ SPURIOUS: overflow=+
|
||||
range(i - n); // $ range=">=Phi: i-99"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestWindowsCmdIgnoreErrors()
|
||||
{
|
||||
actions.RunProcess["cmd.exe /C ^\"build.cmd --skip-tests^\""] = 3;
|
||||
actions.RunProcess["cmd.exe /C ^\"build.cmd^ --skip-tests^\""] = 3;
|
||||
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
@@ -744,9 +744,9 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuild()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -775,9 +775,9 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuildMultipleSolutions()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.csproj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\test1.csproj"] = true;
|
||||
actions.FileExists[@"C:\Project\test2.csproj"] = true;
|
||||
@@ -820,7 +820,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
public void TestWindowCSharpMsBuildFailed()
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -846,8 +846,8 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestSkipNugetMsBuild()
|
||||
{
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
|
||||
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
|
||||
@@ -1037,7 +1037,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
{
|
||||
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj -DisableParallelProcessing"] = 1;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj -DisableParallelProcessing"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\a\test.csproj"] = true;
|
||||
actions.FileExists[@"C:\Project\dirs.proj"] = true;
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Semmle.Autobuild.Shared
|
||||
}
|
||||
|
||||
private static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' };
|
||||
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' };
|
||||
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|', ' ' };
|
||||
|
||||
/// <summary>
|
||||
/// Appends the given argument to the command line.
|
||||
|
||||
@@ -8,14 +8,13 @@ using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Semmle.BuildAnalyser
|
||||
{
|
||||
/// <summary>
|
||||
/// Main implementation of the build analysis.
|
||||
/// </summary>
|
||||
internal sealed partial class BuildAnalysis : IDisposable
|
||||
internal sealed class BuildAnalysis : IDisposable
|
||||
{
|
||||
private readonly AssemblyCache assemblyCache;
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
@@ -29,6 +28,9 @@ namespace Semmle.BuildAnalyser
|
||||
private readonly Options options;
|
||||
private readonly DirectoryInfo sourceDir;
|
||||
private readonly DotNet dotnet;
|
||||
private readonly FileContent fileContent;
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs a C# build analysis.
|
||||
@@ -55,6 +57,9 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
this.progressMonitor.FindingFiles(options.SrcDir);
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
|
||||
|
||||
this.fileContent = new FileContent(packageDirectory, progressMonitor, () => GetFiles("*.*"));
|
||||
this.allSources = GetFiles("*.cs").ToArray();
|
||||
var allProjects = GetFiles("*.csproj");
|
||||
var solutions = options.SolutionFile is not null
|
||||
@@ -63,12 +68,19 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
|
||||
|
||||
// Find DLLs in the .Net Framework
|
||||
// Find DLLs in the .Net / Asp.Net Framework
|
||||
if (options.ScanNetFrameworkDlls)
|
||||
{
|
||||
var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet);
|
||||
progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}");
|
||||
var runtime = new Runtime(dotnet);
|
||||
var runtimeLocation = runtime.GetRuntime(options.UseSelfContainedDotnet);
|
||||
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
|
||||
dllDirNames.Add(runtimeLocation);
|
||||
|
||||
if (fileContent.UseAspNetDlls && runtime.GetAspRuntime() is string aspRuntime)
|
||||
{
|
||||
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspRuntime}");
|
||||
dllDirNames.Add(aspRuntime);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.UseMscorlib)
|
||||
@@ -76,8 +88,6 @@ namespace Semmle.BuildAnalyser
|
||||
UseReference(typeof(object).Assembly.Location);
|
||||
}
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
|
||||
|
||||
if (options.UseNuGet)
|
||||
{
|
||||
dllDirNames.Add(packageDirectory.DirInfo.FullName);
|
||||
@@ -187,6 +197,7 @@ namespace Semmle.BuildAnalyser
|
||||
{
|
||||
finalAssemblyList[r.Name] = r;
|
||||
}
|
||||
|
||||
// Update the used references list
|
||||
usedReferences.Clear();
|
||||
foreach (var r in finalAssemblyList.Select(r => r.Value.Filename))
|
||||
@@ -210,24 +221,18 @@ namespace Semmle.BuildAnalyser
|
||||
/// Store that a particular reference file is used.
|
||||
/// </summary>
|
||||
/// <param name="reference">The filename of the reference.</param>
|
||||
private void UseReference(string reference)
|
||||
{
|
||||
usedReferences[reference] = true;
|
||||
}
|
||||
private void UseReference(string reference) => usedReferences[reference] = true;
|
||||
|
||||
/// <summary>
|
||||
/// Store that a particular source file is used (by a project file).
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
private void UseSource(FileInfo sourceFile)
|
||||
{
|
||||
sources[sourceFile.FullName] = sourceFile.Exists;
|
||||
}
|
||||
private void UseSource(FileInfo sourceFile) => sources[sourceFile.FullName] = sourceFile.Exists;
|
||||
|
||||
/// <summary>
|
||||
/// The list of resolved reference files.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ReferenceFiles => this.usedReferences.Keys;
|
||||
public IEnumerable<string> ReferenceFiles => usedReferences.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// The list of source files used in projects.
|
||||
@@ -242,7 +247,7 @@ namespace Semmle.BuildAnalyser
|
||||
/// <summary>
|
||||
/// List of assembly IDs which couldn't be resolved.
|
||||
/// </summary>
|
||||
public IEnumerable<string> UnresolvedReferences => this.unresolvedReferences.Select(r => r.Key);
|
||||
public IEnumerable<string> UnresolvedReferences => unresolvedReferences.Select(r => r.Key);
|
||||
|
||||
/// <summary>
|
||||
/// List of source files which were mentioned in project files but
|
||||
@@ -256,12 +261,7 @@ namespace Semmle.BuildAnalyser
|
||||
/// </summary>
|
||||
/// <param name="id">The assembly ID.</param>
|
||||
/// <param name="projectFile">The project file making the reference.</param>
|
||||
private void UnresolvedReference(string id, string projectFile)
|
||||
{
|
||||
unresolvedReferences[id] = projectFile;
|
||||
}
|
||||
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
private void UnresolvedReference(string id, string projectFile) => unresolvedReferences[id] = projectFile;
|
||||
|
||||
/// <summary>
|
||||
/// Reads all the source files and references from the given list of projects.
|
||||
@@ -318,10 +318,8 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
}
|
||||
|
||||
private bool Restore(string target, string? pathToNugetConfig = null)
|
||||
{
|
||||
return dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
|
||||
}
|
||||
private bool Restore(string target, string? pathToNugetConfig = null) =>
|
||||
dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
|
||||
|
||||
private void Restore(IEnumerable<string> targets, string? pathToNugetConfig = null)
|
||||
{
|
||||
@@ -331,11 +329,9 @@ namespace Semmle.BuildAnalyser
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DownloadMissingPackages(IEnumerable<string> restoreTargets)
|
||||
{
|
||||
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName).Select(d => Path.GetFileName(d).ToLowerInvariant()).ToHashSet();
|
||||
var notYetDownloadedPackages = new HashSet<string>();
|
||||
|
||||
var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray();
|
||||
string? nugetConfig = null;
|
||||
if (nugetConfigs.Length > 1)
|
||||
@@ -352,46 +348,7 @@ namespace Semmle.BuildAnalyser
|
||||
nugetConfig = nugetConfigs.FirstOrDefault();
|
||||
}
|
||||
|
||||
var allFiles = GetFiles("*.*");
|
||||
foreach (var file in allFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var sr = new StreamReader(file);
|
||||
ReadOnlySpan<char> line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
var match = line.Slice(valueMatch.Index, valueMatch.Length);
|
||||
var includeIndex = match.IndexOf("Include", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (includeIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match = match.Slice(includeIndex + "Include".Length + 1);
|
||||
|
||||
var quoteIndex1 = match.IndexOf("\"");
|
||||
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
|
||||
|
||||
var packageName = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
|
||||
if (!alreadyDownloadedPackages.Contains(packageName))
|
||||
{
|
||||
notYetDownloadedPackages.Add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressMonitor.FailedToReadFile(file, ex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var package in notYetDownloadedPackages)
|
||||
foreach (var package in fileContent.NotYetDownloadedPackages)
|
||||
{
|
||||
progressMonitor.NugetInstall(package);
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
|
||||
@@ -434,12 +391,6 @@ namespace Semmle.BuildAnalyser
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
packageDirectory?.Dispose();
|
||||
}
|
||||
|
||||
[GeneratedRegex("<PackageReference .*Include=\"(.*?)\".*/>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex PackageReference();
|
||||
public void Dispose() => packageDirectory?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Semmle.BuildAnalyser
|
||||
bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null);
|
||||
bool New(string folder);
|
||||
bool AddPackage(string folder, string package);
|
||||
public IList<string> GetListedRuntimes();
|
||||
IList<string> GetListedRuntimes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,7 +78,8 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
public IList<string> GetListedRuntimes()
|
||||
{
|
||||
var args = "--list-runtimes";
|
||||
const string args = "--list-runtimes";
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
var pi = new ProcessStartInfo(dotnet, args)
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
@@ -90,6 +91,7 @@ namespace Semmle.BuildAnalyser
|
||||
progressMonitor.CommandFailed(dotnet, args, exitCode);
|
||||
return new List<string>();
|
||||
}
|
||||
progressMonitor.LogInfo($"Found runtimes: {string.Join("\n", runtimes)}");
|
||||
return runtimes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
using Semmle.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Semmle.BuildAnalyser
|
||||
{
|
||||
|
||||
// <summary>
|
||||
// This class is used to read a set of files and decide different properties about the
|
||||
// content (by reading the content of the files only once).
|
||||
// The implementation is lazy, so the properties are only calculated when
|
||||
// the first property is accessed.
|
||||
// </summary>
|
||||
internal partial class FileContent
|
||||
{
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
private readonly IUnsafeFileReader unsafeFileReader;
|
||||
private readonly Func<IEnumerable<string>> getFiles;
|
||||
private readonly Func<HashSet<string>> getAlreadyDownloadedPackages;
|
||||
private readonly HashSet<string> notYetDownloadedPackages = new HashSet<string>();
|
||||
private readonly Initializer initialize;
|
||||
|
||||
public HashSet<string> NotYetDownloadedPackages
|
||||
{
|
||||
get
|
||||
{
|
||||
initialize.Run();
|
||||
return notYetDownloadedPackages;
|
||||
}
|
||||
}
|
||||
|
||||
private bool useAspNetDlls = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if any file in the source directory indicates that ASP.NET is used.
|
||||
/// The following heuristic is used to decide, if ASP.NET is used:
|
||||
/// If any file in the source directory contains something like (this will most like be a .csproj file)
|
||||
/// <Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
/// <FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||
/// </summary>
|
||||
public bool UseAspNetDlls
|
||||
{
|
||||
get
|
||||
{
|
||||
initialize.Run();
|
||||
return useAspNetDlls;
|
||||
}
|
||||
}
|
||||
|
||||
internal FileContent(Func<HashSet<string>> getAlreadyDownloadedPackages,
|
||||
ProgressMonitor progressMonitor,
|
||||
Func<IEnumerable<string>> getFiles,
|
||||
IUnsafeFileReader unsafeFileReader)
|
||||
{
|
||||
this.getAlreadyDownloadedPackages = getAlreadyDownloadedPackages;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.getFiles = getFiles;
|
||||
this.unsafeFileReader = unsafeFileReader;
|
||||
this.initialize = new Initializer(DoInitialize);
|
||||
}
|
||||
|
||||
|
||||
public FileContent(TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor, Func<IEnumerable<string>> getFiles) : this(() => Directory.GetDirectories(packageDirectory.DirInfo.FullName)
|
||||
.Select(d => Path.GetFileName(d)
|
||||
.ToLowerInvariant())
|
||||
.ToHashSet(), progressMonitor, getFiles, new UnsafeFileReader())
|
||||
{ }
|
||||
|
||||
private static string GetGroup(ReadOnlySpan<char> input, ValueMatch valueMatch, string groupPrefix)
|
||||
{
|
||||
var match = input.Slice(valueMatch.Index, valueMatch.Length);
|
||||
var includeIndex = match.IndexOf(groupPrefix, StringComparison.InvariantCultureIgnoreCase);
|
||||
if (includeIndex == -1)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
match = match.Slice(includeIndex + groupPrefix.Length + 1);
|
||||
|
||||
var quoteIndex1 = match.IndexOf("\"");
|
||||
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
|
||||
|
||||
return match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static bool IsGroupMatch(ReadOnlySpan<char> line, Regex regex, string groupPrefix, string value)
|
||||
{
|
||||
foreach (var valueMatch in regex.EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
if (GetGroup(line, valueMatch, groupPrefix) == value.ToLowerInvariant())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DoInitialize()
|
||||
{
|
||||
var alreadyDownloadedPackages = getAlreadyDownloadedPackages();
|
||||
foreach (var file in getFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (ReadOnlySpan<char> line in unsafeFileReader.ReadLines(file))
|
||||
{
|
||||
|
||||
// Find the not yet downloaded packages.
|
||||
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
|
||||
{
|
||||
// We can't get the group from the ValueMatch, so doing it manually:
|
||||
var packageName = GetGroup(line, valueMatch, "Include");
|
||||
if (!string.IsNullOrEmpty(packageName) && !alreadyDownloadedPackages.Contains(packageName))
|
||||
{
|
||||
notYetDownloadedPackages.Add(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if ASP.NET is used.
|
||||
if (!useAspNetDlls)
|
||||
{
|
||||
useAspNetDlls =
|
||||
IsGroupMatch(line, ProjectSdk(), "Sdk", "Microsoft.NET.Sdk.Web") ||
|
||||
IsGroupMatch(line, FrameworkReference(), "Include", "Microsoft.AspNetCore.App");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressMonitor.FailedToReadFile(file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("<PackageReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex PackageReference();
|
||||
|
||||
[GeneratedRegex("<FrameworkReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex FrameworkReference();
|
||||
|
||||
[GeneratedRegex("<(.*\\s)?Project.*\\sSdk=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
|
||||
private static partial Regex ProjectSdk();
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IUnsafeFileReader
|
||||
{
|
||||
IEnumerable<string> ReadLines(string file);
|
||||
}
|
||||
|
||||
internal class UnsafeFileReader : IUnsafeFileReader
|
||||
{
|
||||
public IEnumerable<string> ReadLines(string file)
|
||||
{
|
||||
using var sr = new StreamReader(file);
|
||||
string? line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,121 +12,101 @@ namespace Semmle.BuildAnalyser
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void FindingFiles(string dir)
|
||||
{
|
||||
logger.Log(Severity.Info, "Finding files in {0}...", dir);
|
||||
}
|
||||
public void Log(Severity severity, string message) =>
|
||||
logger.Log(severity, message);
|
||||
|
||||
public void LogInfo(string message) =>
|
||||
logger.Log(Severity.Info, message);
|
||||
|
||||
private void LogDebug(string message) =>
|
||||
logger.Log(Severity.Debug, message);
|
||||
|
||||
private void LogError(string message) =>
|
||||
logger.Log(Severity.Error, message);
|
||||
|
||||
public void FindingFiles(string dir) =>
|
||||
LogInfo($"Finding files in {dir}...");
|
||||
|
||||
public void IndexingReferences(int count)
|
||||
{
|
||||
logger.Log(Severity.Info, "Indexing...");
|
||||
logger.Log(Severity.Debug, "Indexing {0} DLLs...", count);
|
||||
LogInfo("Indexing...");
|
||||
LogDebug($"Indexing {count} DLLs...");
|
||||
}
|
||||
|
||||
public void UnresolvedReference(string id, string project)
|
||||
{
|
||||
logger.Log(Severity.Info, "Unresolved reference {0}", id);
|
||||
logger.Log(Severity.Debug, "Unresolved {0} referenced by {1}", id, project);
|
||||
LogInfo($"Unresolved reference {id}");
|
||||
LogDebug($"Unresolved {id} referenced by {project}");
|
||||
}
|
||||
|
||||
public void AnalysingSolution(string filename)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Analyzing {filename}...");
|
||||
}
|
||||
public void AnalysingSolution(string filename) =>
|
||||
LogInfo($"Analyzing {filename}...");
|
||||
|
||||
public void FailedProjectFile(string filename, string reason)
|
||||
{
|
||||
logger.Log(Severity.Info, "Couldn't read project file {0}: {1}", filename, reason);
|
||||
}
|
||||
public void FailedProjectFile(string filename, string reason) =>
|
||||
LogInfo($"Couldn't read project file {filename}: {reason}");
|
||||
|
||||
public void FailedNugetCommand(string exe, string args, string message)
|
||||
{
|
||||
logger.Log(Severity.Info, "Command failed: {0} {1}", exe, args);
|
||||
logger.Log(Severity.Info, " {0}", message);
|
||||
LogInfo($"Command failed: {exe} {args}");
|
||||
LogInfo($" {message}");
|
||||
}
|
||||
|
||||
public void NugetInstall(string package)
|
||||
{
|
||||
logger.Log(Severity.Info, "Restoring {0}...", package);
|
||||
}
|
||||
public void NugetInstall(string package) =>
|
||||
LogInfo($"Restoring {package}...");
|
||||
|
||||
public void ResolvedReference(string filename)
|
||||
{
|
||||
logger.Log(Severity.Info, "Resolved {0}", filename);
|
||||
}
|
||||
public void ResolvedReference(string filename) =>
|
||||
LogInfo($"Resolved {filename}");
|
||||
|
||||
public void Summary(int existingSources, int usedSources, int missingSources,
|
||||
int references, int unresolvedReferences,
|
||||
int resolvedConflicts, int totalProjects, int failedProjects,
|
||||
TimeSpan analysisTime)
|
||||
{
|
||||
logger.Log(Severity.Info, "");
|
||||
logger.Log(Severity.Info, "Build analysis summary:");
|
||||
logger.Log(Severity.Info, "{0, 6} source files in the filesystem", existingSources);
|
||||
logger.Log(Severity.Info, "{0, 6} source files in project files", usedSources);
|
||||
logger.Log(Severity.Info, "{0, 6} sources missing from project files", missingSources);
|
||||
logger.Log(Severity.Info, "{0, 6} resolved references", references);
|
||||
logger.Log(Severity.Info, "{0, 6} unresolved references", unresolvedReferences);
|
||||
logger.Log(Severity.Info, "{0, 6} resolved assembly conflicts", resolvedConflicts);
|
||||
logger.Log(Severity.Info, "{0, 6} projects", totalProjects);
|
||||
logger.Log(Severity.Info, "{0, 6} missing/failed projects", failedProjects);
|
||||
logger.Log(Severity.Info, "Build analysis completed in {0}", analysisTime);
|
||||
const int align = 6;
|
||||
LogInfo("");
|
||||
LogInfo("Build analysis summary:");
|
||||
LogInfo($"{existingSources,align} source files in the filesystem");
|
||||
LogInfo($"{usedSources,align} source files in project files");
|
||||
LogInfo($"{missingSources,align} sources missing from project files");
|
||||
LogInfo($"{references,align} resolved references");
|
||||
LogInfo($"{unresolvedReferences,align} unresolved references");
|
||||
LogInfo($"{resolvedConflicts,align} resolved assembly conflicts");
|
||||
LogInfo($"{totalProjects,align} projects");
|
||||
LogInfo($"{failedProjects,align} missing/failed projects");
|
||||
LogInfo($"Build analysis completed in {analysisTime}");
|
||||
}
|
||||
|
||||
public void Log(Severity severity, string message)
|
||||
{
|
||||
logger.Log(severity, message);
|
||||
}
|
||||
public void ResolvedConflict(string asm1, string asm2) =>
|
||||
LogDebug($"Resolved {asm1} as {asm2}");
|
||||
|
||||
public void ResolvedConflict(string asm1, string asm2)
|
||||
{
|
||||
logger.Log(Severity.Debug, "Resolved {0} as {1}", asm1, asm2);
|
||||
}
|
||||
public void MissingProject(string projectFile) =>
|
||||
LogInfo($"Solution is missing {projectFile}");
|
||||
|
||||
public void MissingProject(string projectFile)
|
||||
{
|
||||
logger.Log(Severity.Info, "Solution is missing {0}", projectFile);
|
||||
}
|
||||
public void CommandFailed(string exe, string arguments, int exitCode) =>
|
||||
LogError($"Command {exe} {arguments} failed with exit code {exitCode}");
|
||||
|
||||
public void CommandFailed(string exe, string arguments, int exitCode)
|
||||
{
|
||||
logger.Log(Severity.Error, $"Command {exe} {arguments} failed with exit code {exitCode}");
|
||||
}
|
||||
public void MissingNuGet() =>
|
||||
LogError("Missing nuget.exe");
|
||||
|
||||
public void MissingNuGet()
|
||||
{
|
||||
logger.Log(Severity.Error, "Missing nuget.exe");
|
||||
}
|
||||
public void MissingDotNet() =>
|
||||
LogError("Missing dotnet CLI");
|
||||
|
||||
public void MissingDotNet()
|
||||
{
|
||||
logger.Log(Severity.Error, "Missing dotnet CLI");
|
||||
}
|
||||
public void RunningProcess(string command) =>
|
||||
LogInfo($"Running {command}");
|
||||
|
||||
public void RunningProcess(string command)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Running {command}");
|
||||
}
|
||||
|
||||
public void FailedToRestoreNugetPackage(string package)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Failed to restore nuget package {package}");
|
||||
}
|
||||
public void FailedToRestoreNugetPackage(string package) =>
|
||||
LogInfo($"Failed to restore nuget package {package}");
|
||||
|
||||
public void FailedToReadFile(string file, Exception ex)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Failed to read file {file}");
|
||||
logger.Log(Severity.Debug, $"Failed to read file {file}, exception: {ex}");
|
||||
LogInfo($"Failed to read file {file}");
|
||||
LogDebug($"Failed to read file {file}, exception: {ex}");
|
||||
}
|
||||
|
||||
public void MultipleNugetConfig(string[] nugetConfigs)
|
||||
{
|
||||
logger.Log(Severity.Info, $"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
|
||||
}
|
||||
public void MultipleNugetConfig(string[] nugetConfigs) =>
|
||||
LogInfo($"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
|
||||
|
||||
internal void NoTopLevelNugetConfig()
|
||||
{
|
||||
logger.Log(Severity.Info, $"Could not find a top-level nuget.config file.");
|
||||
}
|
||||
internal void NoTopLevelNugetConfig() =>
|
||||
LogInfo("Could not find a top-level nuget.config file.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,15 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
private const string aspNetCoreApp = "Microsoft.AspNetCore.App";
|
||||
|
||||
private readonly IDotNet dotNet;
|
||||
private readonly Lazy<Dictionary<string, RuntimeVersion>> newestRuntimes;
|
||||
private Dictionary<string, RuntimeVersion> NewestRuntimes => newestRuntimes.Value;
|
||||
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
|
||||
|
||||
public Runtime(IDotNet dotNet) => this.dotNet = dotNet;
|
||||
public Runtime(IDotNet dotNet)
|
||||
{
|
||||
this.dotNet = dotNet;
|
||||
this.newestRuntimes = new(GetNewestRuntimes);
|
||||
}
|
||||
|
||||
internal record RuntimeVersion : IComparable<RuntimeVersion>
|
||||
{
|
||||
@@ -74,7 +80,7 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
public override string ToString() => FullPath;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(\S+)\]$")]
|
||||
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(.+)\]$")]
|
||||
private static partial Regex RuntimeRegex();
|
||||
|
||||
/// <summary>
|
||||
@@ -140,33 +146,42 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetRuntimes()
|
||||
/// <summary>
|
||||
/// Gets the .NET runtime location to use for extraction.
|
||||
/// </summary>
|
||||
public string GetRuntime(bool useSelfContained)
|
||||
{
|
||||
// Gets the newest version of the installed runtimes.
|
||||
var newestRuntimes = GetNewestRuntimes();
|
||||
if (useSelfContained)
|
||||
{
|
||||
return ExecutingRuntime;
|
||||
}
|
||||
|
||||
// Location of the newest .NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
|
||||
if (NewestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
|
||||
{
|
||||
yield return netCoreVersion.FullPath;
|
||||
return netCoreVersion.FullPath;
|
||||
}
|
||||
|
||||
// Location of the newest ASP.NET Core Runtime.
|
||||
if (newestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
|
||||
if (DesktopRuntimes.Any())
|
||||
{
|
||||
yield return aspNetCoreVersion.FullPath;
|
||||
return DesktopRuntimes.First();
|
||||
}
|
||||
|
||||
foreach (var r in DesktopRuntimes)
|
||||
yield return r;
|
||||
|
||||
// A bad choice if it's the self-contained runtime distributed in codeql dist.
|
||||
yield return ExecutingRuntime;
|
||||
return ExecutingRuntime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the .NET runtime location to use for extraction
|
||||
/// Gets the ASP.NET runtime location to use for extraction, if one exists.
|
||||
/// </summary>
|
||||
public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First();
|
||||
public string? GetAspRuntime()
|
||||
{
|
||||
// Location of the newest ASP.NET Core Runtime.
|
||||
if (NewestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
|
||||
{
|
||||
return aspNetCoreVersion.FullPath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
Normal file
95
csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Xunit;
|
||||
using Semmle.BuildAnalyser;
|
||||
using Semmle.Util.Logging;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Extraction.Tests
|
||||
{
|
||||
|
||||
internal class LoggerStub : ILogger
|
||||
{
|
||||
public void Log(Severity severity, string message) { }
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
internal class UnsafeFileReaderStub : IUnsafeFileReader
|
||||
{
|
||||
private readonly List<string> lines;
|
||||
|
||||
public UnsafeFileReaderStub(List<string> lines)
|
||||
{
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
public IEnumerable<string> ReadLines(string file)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestFileContent : FileContent
|
||||
{
|
||||
public TestFileContent(List<string> lines) : base(() => new HashSet<string>(),
|
||||
new ProgressMonitor(new LoggerStub()),
|
||||
() => new List<string>() { "test1.cs" },
|
||||
new UnsafeFileReaderStub(lines))
|
||||
{ }
|
||||
}
|
||||
|
||||
public class FileContentTests
|
||||
{
|
||||
[Fact]
|
||||
public void TestFileContent1()
|
||||
{
|
||||
// Setup
|
||||
var lines = new List<string>()
|
||||
{
|
||||
"<Project Sdk=\"Microsoft.NET.Sdk\">",
|
||||
"<PackageReference Include=\"DotNetAnalyzers.DocumentationAnalyzers\" Version=\"1.0.0-beta.59\" PrivateAssets=\"all\" />",
|
||||
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
|
||||
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">",
|
||||
"<FrameworkReference Include=\"My.Framework\"/>"
|
||||
};
|
||||
var fileContent = new TestFileContent(lines);
|
||||
|
||||
// Execute
|
||||
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
|
||||
var useAspNetDlls = fileContent.UseAspNetDlls;
|
||||
|
||||
// Verify
|
||||
Assert.False(useAspNetDlls);
|
||||
Assert.Equal(3, notYetDownloadedPackages.Count);
|
||||
Assert.Contains("DotNetAnalyzers.DocumentationAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestFileContent2()
|
||||
{
|
||||
// Setup
|
||||
var lines = new List<string>()
|
||||
{
|
||||
"<Project Sdk=\"Microsoft.NET.Sdk.Web\">",
|
||||
"<FrameworkReference Include=\"My.Framework\"/>",
|
||||
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
|
||||
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">"
|
||||
};
|
||||
var fileContent = new TestFileContent(lines);
|
||||
|
||||
// Execute
|
||||
var useAspNetDlls = fileContent.UseAspNetDlls;
|
||||
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
|
||||
|
||||
// Verify
|
||||
Assert.True(useAspNetDlls);
|
||||
Assert.Equal(2, notYetDownloadedPackages.Count);
|
||||
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,5 +100,36 @@ namespace Semmle.Extraction.Tests
|
||||
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-rc.4.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestRuntime4()
|
||||
{
|
||||
// Setup
|
||||
var listedRuntimes = new List<string>
|
||||
{
|
||||
@"Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.AspNetCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.AspNetCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
|
||||
@"Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.NETCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.NETCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
|
||||
@"Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
|
||||
@"Microsoft.WindowsDesktop.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
|
||||
@"Microsoft.WindowsDesktop.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]"
|
||||
};
|
||||
var dotnet = new DotNetStub(listedRuntimes);
|
||||
var runtime = new Runtime(dotnet);
|
||||
|
||||
// Execute
|
||||
var runtimes = runtime.GetNewestRuntimes();
|
||||
|
||||
// Verify
|
||||
Assert.Equal(3, runtimes.Count);
|
||||
|
||||
Assert.True(runtimes.TryGetValue("Microsoft.AspNetCore.App", out var aspNetCoreApp));
|
||||
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App/7.0.2", FixExpectedPathOnWindows(aspNetCoreApp.FullPath));
|
||||
|
||||
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
|
||||
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.NETCore.App/7.0.2", FixExpectedPathOnWindows(netCoreApp.FullPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
csharp/extractor/Semmle.Util/Initializer.cs
Normal file
28
csharp/extractor/Semmle.Util/Initializer.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Semmle.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// An instance of this class is used to ensure that the provided
|
||||
/// action is executed only once and on the first call to `Run`.
|
||||
/// It is thread-safe.
|
||||
/// </summary>
|
||||
public class Initializer
|
||||
{
|
||||
private readonly Lazy<bool> doInit;
|
||||
|
||||
public Initializer(Action action)
|
||||
{
|
||||
doInit = new Lazy<bool>(() =>
|
||||
{
|
||||
action();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var _ = doInit.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
| Program.cs:0:0:0:0 | Program.cs |
|
||||
@@ -0,0 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from File f
|
||||
where f.fromSource()
|
||||
select f
|
||||
@@ -0,0 +1 @@
|
||||
var dummy = "dummy";
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
|
||||
<RemoveDir Directories=".\bin" />
|
||||
<RemoveDir Directories=".\obj" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
from create_database_utils import *
|
||||
|
||||
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])
|
||||
@@ -397,6 +397,8 @@ The following components are supported:
|
||||
- **SyntheticGlobal[**\ `name`\ **]** selects the synthetic global with name `name`.
|
||||
- **ArrayElement** selects the elements of an array.
|
||||
- **Element** selects the elements of a collection-like container.
|
||||
- **WithoutElement** selects a collection-like container without its elements. This is for input only.
|
||||
- **WithElement** selects the elements of a collection-like container, but points to the container itself. This is for input only.
|
||||
- **MapKey** selects the element keys of a map.
|
||||
- **MapValue** selects the element values of a map.
|
||||
|
||||
|
||||
@@ -122,12 +122,19 @@ Global environments
|
||||
|
||||
The global module environment has a single entry ``QlBuiltins``.
|
||||
|
||||
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``, as well as any types defined in the database schema.
|
||||
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``.
|
||||
|
||||
The global predicate environment includes all the built-in classless predicates, as well as any extensional predicates declared in the database schema.
|
||||
The global predicate environment includes all the built-in classless predicates.
|
||||
|
||||
The three global signature environments are empty.
|
||||
|
||||
Database schema environments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The database schema type environment has entries for types declared in the database schema.
|
||||
|
||||
The database schema predicate environment has entries for extensional predicates declared in the database schema.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
Module environments
|
||||
@@ -146,7 +153,7 @@ These are defined as follows (with X denoting the type of entity we are currentl
|
||||
|
||||
2. for each module which the current module directly imports (excluding ``private`` imports - see "`Import directives <#import-directives>`__"): all entries from the *exported X environment* that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
3. if X is ``predicates``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
|
||||
3. if X is ``predicate``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
|
||||
|
||||
- The *visible X environment* of a module is the union of
|
||||
|
||||
@@ -160,7 +167,9 @@ These are defined as follows (with X denoting the type of entity we are currentl
|
||||
|
||||
5. if there is an enclosing module: all entries from the *visible X environment* of the enclosing module that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
6. all parameters of the current module that are of type X.
|
||||
6. if there is no enclosing module and X is either ``type`` or ``predicate``: all entries from the *database schema X environment* that have a key not present in the *publically declared X environment* of the current module, and
|
||||
|
||||
7. all parameters of the current module that are of type X.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
|
||||
@@ -773,7 +773,7 @@ func installDependenciesAndBuild() {
|
||||
|
||||
goModVersion, goModVersionFound := tryReadGoDirective(buildInfo)
|
||||
|
||||
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 {
|
||||
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) > 0 {
|
||||
diagnostics.EmitNewerGoVersionNeeded()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Add support for `WithElement` and `WithoutElement` for MaD access paths.
|
||||
@@ -140,7 +140,8 @@ extensions:
|
||||
- ["java.util", "LinkedHashSet", False, "LinkedHashSet", "(Collection)", "", "Argument[0].Element", "Argument[this].Element", "value", "manual"]
|
||||
- ["java.util", "LinkedList", False, "LinkedList", "(Collection)", "", "Argument[0].Element", "Argument[this].Element", "value", "manual"]
|
||||
- ["java.util", "List", True, "add", "(int,Object)", "", "Argument[1]", "Argument[this].Element", "value", "manual"]
|
||||
- ["java.util", "List", True, "addAll", "(int,Collection)", "", "Argument[1].Element", "Argument[this].Element", "value", "manual"]
|
||||
- ["java.util", "List", True, "addAll", "(int,Collection)", "", "Argument[1].WithElement", "Argument[this]", "value", "manual"]
|
||||
- ["java.util", "List", True, "clear", "()", "", "Argument[this].WithoutElement", "Argument[this]", "value", "manual"]
|
||||
- ["java.util", "List", False, "copyOf", "(Collection)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.util", "List", True, "get", "(int)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "List", True, "listIterator", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"]
|
||||
@@ -313,6 +314,7 @@ extensions:
|
||||
- ["java.util", "Scanner", True, "useLocale", "", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Scanner", True, "useRadix", "", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Set", False, "copyOf", "(Collection)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.util", "Set", False, "clear", "()", "", "Argument[this].WithoutElement", "Argument[this]", "value", "manual"]
|
||||
- ["java.util", "Set", False, "of", "(Object)", "", "Argument[0]", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.util", "Set", False, "of", "(Object,Object)", "", "Argument[0..1]", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.util", "Set", False, "of", "(Object,Object,Object)", "", "Argument[0..2]", "ReturnValue.Element", "value", "manual"]
|
||||
@@ -424,10 +426,8 @@ extensions:
|
||||
# When `WithoutElement` is implemented, these should be changed to summary models of the form `Argument[this].WithoutElement -> Argument[this]`.
|
||||
- ["java.util", "Collection", "removeIf", "(Predicate)", "summary", "manual"]
|
||||
- ["java.util", "Iterator", "remove", "()", "summary", "manual"]
|
||||
- ["java.util", "List", "clear", "()", "summary", "manual"]
|
||||
- ["java.util", "List", "remove", "(Object)", "summary", "manual"]
|
||||
- ["java.util", "Map", "clear", "()", "summary", "manual"]
|
||||
- ["java.util", "Set", "clear", "()", "summary", "manual"]
|
||||
- ["java.util", "Set", "remove", "(Object)", "summary", "manual"]
|
||||
- ["java.util", "Set", "removeAll", "(Collection)", "summary", "manual"]
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@
|
||||
* or method, or a parameter.
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
* first 6 columns, and the `output` column specifies how data leaves the
|
||||
* element selected by the first 6 columns. An `input` can be either "",
|
||||
* "Argument[n]", "Argument[n1..n2]", "ReturnValue":
|
||||
* element selected by the first 6 columns. An `input` can be a dot separated
|
||||
* path consisting of either "", "Argument[n]", "Argument[n1..n2]",
|
||||
* "ReturnValue", "Element", "WithoutElement", or "WithElement":
|
||||
* - "": Selects a write to the selected element in case this is a field.
|
||||
* - "Argument[n]": Selects an argument in a call to the selected element.
|
||||
* The arguments are zero-indexed, and `this` specifies the qualifier.
|
||||
@@ -44,9 +45,15 @@
|
||||
* the given range. The range is inclusive at both ends.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* This requires that the selected element is a method with a body.
|
||||
* - "Element": Selects the collection elements of the selected element.
|
||||
* - "WithoutElement": Selects the selected element but without
|
||||
* its collection elements.
|
||||
* - "WithElement": Selects the collection elements of the selected element, but
|
||||
* points to the selected element.
|
||||
*
|
||||
* An `output` can be either "", "Argument[n]", "Argument[n1..n2]", "Parameter",
|
||||
* "Parameter[n]", "Parameter[n1..n2]", or "ReturnValue":
|
||||
* An `output` can be can be a dot separated path consisting of either "",
|
||||
* "Argument[n]", "Argument[n1..n2]", "Parameter", "Parameter[n]",
|
||||
* "Parameter[n1..n2]", "ReturnValue", or "Element":
|
||||
* - "": Selects a read of a selected field, or a selected parameter.
|
||||
* - "Argument[n]": Selects the post-update value of an argument in a call to the
|
||||
* selected element. That is, the value of the argument after the call returns.
|
||||
@@ -61,6 +68,7 @@
|
||||
* - "Parameter[n1..n2]": Similar to "Parameter[n]" but selects any parameter
|
||||
* in the given range. The range is inclusive at both ends.
|
||||
* - "ReturnValue": Selects the return value of a call to the selected element.
|
||||
* - "Element": Selects the collection elements of the selected element.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
|
||||
@@ -170,6 +170,10 @@ predicate neutralSummaryElement(SummarizedCallableBase c, string provenance) {
|
||||
bindingset[c]
|
||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content))
|
||||
or
|
||||
c = "WithoutElement" and result = SummaryComponent::withoutContent(any(CollectionContent cc))
|
||||
or
|
||||
c = "WithElement" and result = SummaryComponent::withContent(any(CollectionContent cc))
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
@@ -196,6 +200,10 @@ private string getContentSpecific(Content c) {
|
||||
/** Gets the textual representation of the content in the format used for MaD models. */
|
||||
string getMadRepresentationSpecific(SummaryComponent sc) {
|
||||
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
|
||||
or
|
||||
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"
|
||||
or
|
||||
sc = TWithContentSummaryComponent(_) and result = "WithElement"
|
||||
}
|
||||
|
||||
bindingset[pos]
|
||||
|
||||
@@ -21,21 +21,97 @@ import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
newtype JavaRelatedLocationType = CallContext()
|
||||
|
||||
newtype TApplicationModeEndpoint =
|
||||
TExplicitArgument(Call call, DataFlow::Node arg) {
|
||||
exists(Argument argExpr |
|
||||
arg.asExpr() = argExpr and call = argExpr.getCall() and not argExpr.isVararg()
|
||||
)
|
||||
} or
|
||||
TInstanceArgument(Call call, DataFlow::Node arg) { arg = DataFlow::getInstanceArgument(call) } or
|
||||
TImplicitVarargsArray(Call call, DataFlow::Node arg, int idx) {
|
||||
exists(Argument argExpr |
|
||||
arg.asExpr() = argExpr and
|
||||
call.getArgument(idx) = argExpr and
|
||||
argExpr.isVararg() and
|
||||
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint is a node that is a candidate for modeling.
|
||||
*/
|
||||
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
|
||||
abstract predicate isArgOf(Call c, int idx);
|
||||
|
||||
Call getCall() { this.isArgOf(result, _) }
|
||||
|
||||
int getArgIndex() { this.isArgOf(_, result) }
|
||||
|
||||
abstract Top asTop();
|
||||
|
||||
abstract DataFlow::Node asNode();
|
||||
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A class representing nodes that are arguments to calls.
|
||||
*/
|
||||
private class ArgumentNode extends DataFlow::Node {
|
||||
Call c;
|
||||
class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
|
||||
Call call;
|
||||
DataFlow::Node arg;
|
||||
|
||||
ArgumentNode() {
|
||||
exists(Argument arg | this.asExpr() = arg and not arg.isVararg() and c = arg.getCall())
|
||||
or
|
||||
this.(DataFlow::ImplicitVarargsArray).getCall() = c
|
||||
or
|
||||
this = DataFlow::getInstanceArgument(c)
|
||||
ExplicitArgument() { this = TExplicitArgument(call, arg) }
|
||||
|
||||
override predicate isArgOf(Call c, int idx) { c = call and this.asTop() = c.getArgument(idx) }
|
||||
|
||||
override Top asTop() { result = arg.asExpr() }
|
||||
|
||||
override DataFlow::Node asNode() { result = arg }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
|
||||
Call call;
|
||||
DataFlow::Node arg;
|
||||
|
||||
InstanceArgument() { this = TInstanceArgument(call, arg) }
|
||||
|
||||
override predicate isArgOf(Call c, int idx) {
|
||||
c = call and this.asTop() = c.getQualifier() and idx = -1
|
||||
}
|
||||
|
||||
Call getCall() { result = c }
|
||||
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result = arg }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents an implicit varargs array.
|
||||
* We choose to represent the varargs array as a single endpoint, rather than as multiple endpoints.
|
||||
*
|
||||
* This avoids the problem of having to deal with redundant endpoints downstream.
|
||||
*
|
||||
* In order to be able to distinguish between varargs endpoints and regular endpoints, we export the `isVarargsArray`
|
||||
* meta data field in the extraction queries.
|
||||
*/
|
||||
class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArray {
|
||||
Call call;
|
||||
DataFlow::Node vararg;
|
||||
int idx;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
|
||||
|
||||
override predicate isArgOf(Call c, int i) { c = call and i = idx }
|
||||
|
||||
override Top asTop() { result = this.getCall() }
|
||||
|
||||
override DataFlow::Node asNode() { result = vararg }
|
||||
|
||||
override string toString() { result = vararg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +123,7 @@ private class ArgumentNode extends DataFlow::Node {
|
||||
*/
|
||||
module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
|
||||
class Endpoint = ArgumentNode;
|
||||
class Endpoint = ApplicationModeEndpoint;
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
@@ -61,18 +137,18 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
predicate isSanitizer(Endpoint e, EndpointType t) {
|
||||
exists(t) and
|
||||
(
|
||||
e.getType() instanceof BoxedType
|
||||
e.asNode().getType() instanceof BoxedType
|
||||
or
|
||||
e.getType() instanceof PrimitiveType
|
||||
e.asNode().getType() instanceof PrimitiveType
|
||||
or
|
||||
e.getType() instanceof NumberType
|
||||
e.asNode().getType() instanceof NumberType
|
||||
)
|
||||
or
|
||||
t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
|
||||
e instanceof PathSanitizer::PathInjectionSanitizer
|
||||
e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
|
||||
}
|
||||
|
||||
RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
|
||||
RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
|
||||
|
||||
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
|
||||
|
||||
@@ -98,16 +174,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
|
||||
ext = "" and
|
||||
(
|
||||
exists(Call c, int argIdx |
|
||||
e.asExpr() = c.getArgument(argIdx) and
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(argIdx)
|
||||
)
|
||||
or
|
||||
exists(Call c |
|
||||
e.asExpr() = c.getQualifier() and input = AutomodelJavaUtil::getArgumentForIndex(-1)
|
||||
)
|
||||
)
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +185,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = CallContext() and
|
||||
result = any(Call c | e.asExpr() = [c.getAnArgument(), c.getQualifier()])
|
||||
result = e.getCall()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,12 +199,7 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable:
|
||||
/**
|
||||
* Returns the API callable being modeled.
|
||||
*/
|
||||
Callable getCallable(Endpoint e) {
|
||||
exists(Call c |
|
||||
e.asExpr() = [c.getAnArgument(), c.getQualifier()] and
|
||||
result = c.getCallee()
|
||||
)
|
||||
}
|
||||
Callable getCallable(Endpoint e) { result = e.getCall().getCallee() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +207,7 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable:
|
||||
* should be empty.
|
||||
*/
|
||||
private predicate isCustomSink(Endpoint e, string kind) {
|
||||
e instanceof QueryInjectionSink and kind = "sql"
|
||||
e.asNode() instanceof QueryInjectionSink and kind = "sql"
|
||||
}
|
||||
|
||||
module CharacteristicsImpl =
|
||||
@@ -167,23 +229,21 @@ class ApplicationModeMetadataExtractor extends string {
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input
|
||||
string input, string isVarargsArray
|
||||
) {
|
||||
exists(Call call, Callable callable, int argIdx |
|
||||
call.getCallee() = callable and
|
||||
(
|
||||
e.asExpr() = call.getArgument(argIdx)
|
||||
or
|
||||
e.asExpr() = call.getQualifier() and argIdx = -1
|
||||
) and
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(argIdx) and
|
||||
exists(Callable callable |
|
||||
e.getCall().getCallee() = callable and
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex()) and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
// we're using the erased types because the MaD convention is to not specify type parameters.
|
||||
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
|
||||
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
|
||||
subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
|
||||
name = callable.getName() and
|
||||
signature = ExternalFlow::paramsString(callable)
|
||||
signature = ExternalFlow::paramsString(callable) and
|
||||
if e instanceof ImplicitVarargsArray
|
||||
then isVarargsArray = "true"
|
||||
else isVarargsArray = "false"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -253,28 +313,10 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
|
||||
IsMaDTaintStepCharacteristic() { this = "taint step" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e, _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e, _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(e, _, _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(e, _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
|
||||
* are unlikely to have any non-trivial flow going into them.
|
||||
*
|
||||
* Technically, an accessed type _could_ come from outside of the source code, but there's not
|
||||
* much likelihood of that being user-controlled.
|
||||
*/
|
||||
private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
ClassQualifierCharacteristic() { this = "class qualifier" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
exists(Call c |
|
||||
e.asExpr() = c.getQualifier() and
|
||||
c.getQualifier() instanceof TypeAccess
|
||||
)
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +393,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
|
||||
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
|
||||
FunctionValueCharacteristic() { this = "function value" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) { e.asExpr() instanceof FunctionalExpr }
|
||||
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,12 +413,12 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
*/
|
||||
private predicate isKnownOutNodeForStep(Endpoint e) {
|
||||
e.asExpr() instanceof Call or // we just assume flow in that case
|
||||
TaintTracking::localTaintStep(_, e) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e, _) or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e, _)
|
||||
e.asNode().asExpr() instanceof Call or // we just assume flow in that case
|
||||
TaintTracking::localTaintStep(_, e.asNode()) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e.asNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,16 +25,18 @@ private import AutomodelJavaUtil
|
||||
bindingset[limit]
|
||||
private Endpoint getSampleForSignature(
|
||||
int limit, string package, string type, string subtypes, string name, string signature,
|
||||
string input
|
||||
string input, string isVarargs
|
||||
) {
|
||||
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
|
||||
num_endpoints =
|
||||
count(Endpoint e | meta.hasMetadata(e, package, type, subtypes, name, signature, input))
|
||||
count(Endpoint e |
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
|
||||
)
|
||||
|
|
||||
result =
|
||||
rank[n](Endpoint e, Location loc |
|
||||
loc = e.getLocation() and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input)
|
||||
loc = e.asTop().getLocation() and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
|
||||
|
|
||||
e
|
||||
order by
|
||||
@@ -53,19 +55,20 @@ private Endpoint getSampleForSignature(
|
||||
from
|
||||
Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package,
|
||||
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
|
||||
DollarAtString input
|
||||
DollarAtString input, DollarAtString isVarargsArray
|
||||
where
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
endpoint = getSampleForSignature(9, package, type, subtypes, name, signature, input) and
|
||||
endpoint =
|
||||
getSampleForSignature(9, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
|
||||
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
|
||||
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
|
||||
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
|
||||
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
|
||||
not CharacteristicsImpl::isSink(endpoint, _, _) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
includeAutomodelCandidate(package, type, name, signature) and
|
||||
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
|
||||
// a non-sink, and we surface only endpoints that have at least one such sink type.
|
||||
@@ -76,11 +79,13 @@ where
|
||||
|
|
||||
sinkType, ", "
|
||||
)
|
||||
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", // method name
|
||||
signature, "signature", //
|
||||
input, "input" //
|
||||
input, "input", //
|
||||
isVarargsArray, "isVarargsArray"
|
||||
|
||||
@@ -24,7 +24,7 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
|
||||
exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
|
||||
result =
|
||||
rank[n](Endpoint e, Location loc |
|
||||
loc = e.getLocation() and c.appliesToEndpoint(e)
|
||||
loc = e.asTop().getLocation() and c.appliesToEndpoint(e)
|
||||
|
|
||||
e
|
||||
order by
|
||||
@@ -43,7 +43,8 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
|
||||
from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
|
||||
ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
|
||||
DollarAtString isVarargsArray
|
||||
where
|
||||
endpoint = getSampleForCharacteristic(characteristic, 100) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
@@ -51,7 +52,7 @@ where
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
|
||||
// treated by the actual query as a sanitizer, since the final logic is something like
|
||||
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
|
||||
@@ -63,11 +64,13 @@ where
|
||||
characteristic2.hasImplications(positiveType, true, confidence2)
|
||||
) and
|
||||
message = characteristic
|
||||
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input" //
|
||||
input, "input", //
|
||||
isVarargsArray, "isVarargsArray" //
|
||||
|
||||
@@ -15,19 +15,22 @@ private import AutomodelJavaUtil
|
||||
from
|
||||
Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta,
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input
|
||||
DollarAtString signature, DollarAtString input, DollarAtString isVarargsArray
|
||||
where
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownSink(endpoint, sinkType)
|
||||
select endpoint, sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::isKnownSink(endpoint, sinkType) and
|
||||
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
|
||||
select endpoint.asNode(),
|
||||
sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input" //
|
||||
input, "input", //
|
||||
isVarargsArray, "isVarargsArray"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
| java.time | 0 | 0 | 0 | 17 | 17 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
|
||||
| java.time.chrono | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
|
||||
| java.time.format | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
|
||||
| java.util | 0 | 0 | 84 | 68 | 152 | 0.5526315789473685 | 0.0 | 0.5526315789473685 | 0.0 | NaN | 0.4473684210526316 |
|
||||
| java.util | 0 | 0 | 86 | 66 | 152 | 0.5657894736842105 | 0.0 | 0.5657894736842105 | 0.0 | NaN | 0.4342105263157895 |
|
||||
| java.util.concurrent | 0 | 0 | 9 | 9 | 18 | 0.5 | 0.0 | 0.5 | 0.0 | NaN | 0.5 |
|
||||
| java.util.concurrent.atomic | 0 | 0 | 2 | 11 | 13 | 0.15384615384615385 | 0.0 | 0.15384615384615385 | 0.0 | NaN | 0.8461538461538461 |
|
||||
| java.util.concurrent.locks | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
|
||||
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
|
||||
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:53:4:53:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://true:1:1:1:1 | true | isVarargsArray |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| Test.java:40:14:40:21 | openPath | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:40:4:40:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
|
||||
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
|
||||
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:52:4:52:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
|
||||
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input |
|
||||
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
|
||||
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.nio.file.Paths;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import java.io.File;
|
||||
|
||||
import java.nio.file.FileVisitOption;
|
||||
|
||||
class Test {
|
||||
public static void main(String[] args) throws Exception {
|
||||
@@ -46,5 +46,14 @@ class Test {
|
||||
f2 // negative example (modeled as not a sink)
|
||||
);
|
||||
}
|
||||
|
||||
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
|
||||
Files.walk(
|
||||
p, // negative example (modeled as a taint step)
|
||||
o, // the implicit varargs array is a candidate
|
||||
o // not a candidate (only the first arg corresponding to a varargs array
|
||||
// is extracted)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -445,7 +445,7 @@
|
||||
| tst.js:146:15:146:21 | (\\d\|5)* | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding ((\\d\|5)*)" |
|
||||
| tst.js:149:15:149:24 | (\\s\|[\\f])* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding ((\\s\|[\\f])*)" |
|
||||
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding ((\\s\|[\\v]\|\\\\v)*)" |
|
||||
| tst.js:155:15:155:24 | (\\f\|[\\f])* | Strings with many repetitions of '\u000c' can start matching anywhere after the start of the preceeding ((\\f\|[\\f])*)" |
|
||||
| tst.js:155:15:155:24 | (\\f\|[\\f])* | Strings with many repetitions of '\\u000c' can start matching anywhere after the start of the preceeding ((\\f\|[\\f])*)" |
|
||||
| tst.js:158:15:158:22 | (\\W\|\\D)* | Strings with many repetitions of '/' can start matching anywhere after the start of the preceeding ((\\W\|\\D)*)" |
|
||||
| tst.js:161:15:161:22 | (\\S\|\\w)* | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding ((\\S\|\\w)*)" |
|
||||
| tst.js:164:15:164:24 | (\\S\|[\\w])* | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding ((\\S\|[\\w])*)" |
|
||||
|
||||
@@ -123,9 +123,9 @@
|
||||
| tst.js:137:15:137:21 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
|
||||
| tst.js:143:15:143:22 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| tst.js:146:15:146:21 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
|
||||
| tst.js:149:15:149:24 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
|
||||
| tst.js:155:15:155:24 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| tst.js:149:15:149:24 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
|
||||
| tst.js:155:15:155:24 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| tst.js:158:15:158:22 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '/'. |
|
||||
| tst.js:161:15:161:22 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| tst.js:164:15:164:24 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
@@ -199,3 +199,5 @@
|
||||
| tst.js:404:6:405:7 | (g\|gg)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'gg'. |
|
||||
| tst.js:407:125:407:127 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '0/*' and containing many repetitions of ' ;0'. |
|
||||
| tst.js:411:15:411:19 | a{1,} | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
|
||||
| tst.js:413:25:413:35 | (\\u0000\|.)+ | This part of the regular expression may cause exponential backtracking on strings starting with '\\n\\u0000' and containing many repetitions of '\\u0000'. |
|
||||
| tst.js:415:44:415:57 | (\ud83d\ude80\|.)+ | This part of the regular expression may cause exponential backtracking on strings starting with '\\n\\u{1f680}' and containing many repetitions of '\\u{1f680}'. |
|
||||
|
||||
@@ -408,4 +408,8 @@ var bad98 = /^(?:\*\/\*|[a-zA-Z0-9][a-zA-Z0-9!\#\$&\-\^_\.\+]{0,126}\/(?:\*|[a-z
|
||||
|
||||
var good48 = /(\/(?:\/[\w.-]*)*){0,1}:([\w.-]+)/;
|
||||
|
||||
var bad99 = /(a{1,})*b/;
|
||||
var bad99 = /(a{1,})*b/;
|
||||
|
||||
var unicode = /^\n\u0000(\u0000|.)+$/;
|
||||
|
||||
var largeUnicode = new RegExp("^\n\u{1F680}(\u{1F680}|.)+X$");
|
||||
@@ -51,6 +51,12 @@ def _get_includes(includes):
|
||||
def _cmake_aspect_impl(target, ctx):
|
||||
if not ctx.rule.kind.startswith("cc_"):
|
||||
return [CmakeInfo(name = None, transitive_deps = depset())]
|
||||
if ctx.rule.kind == "cc_binary_add_features":
|
||||
dep = ctx.rule.attr.dep[0][CmakeInfo]
|
||||
return [CmakeInfo(
|
||||
name = None,
|
||||
transitive_deps = depset([dep], transitive = [dep.transitive_deps]),
|
||||
)]
|
||||
|
||||
name = _cmake_name(ctx.label)
|
||||
|
||||
@@ -146,7 +152,7 @@ def _cmake_aspect_impl(target, ctx):
|
||||
|
||||
cmake_aspect = aspect(
|
||||
implementation = _cmake_aspect_impl,
|
||||
attr_aspects = ["deps"],
|
||||
attr_aspects = ["deps", "dep"],
|
||||
fragments = ["cpp"],
|
||||
)
|
||||
|
||||
@@ -156,10 +162,10 @@ def _map_cmake_info(info, is_windows):
|
||||
"add_%s(%s)" % (info.kind, args),
|
||||
]
|
||||
if info.imported_libs:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_link_libraries(%s %s %s)" %
|
||||
(info.name, info.modifier or "PUBLIC", " ".join(info.imported_libs)),
|
||||
]
|
||||
)
|
||||
if info.deps:
|
||||
libs = {}
|
||||
if info.modifier == "INTERFACE":
|
||||
@@ -168,46 +174,46 @@ def _map_cmake_info(info, is_windows):
|
||||
for lib in info.deps:
|
||||
libs.setdefault(lib.modifier, []).append(lib.name)
|
||||
for modifier, names in libs.items():
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_link_libraries(%s %s %s)" % (info.name, modifier or "PUBLIC", " ".join(names)),
|
||||
]
|
||||
)
|
||||
if info.includes:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_include_directories(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.includes)),
|
||||
]
|
||||
)
|
||||
if info.system_includes:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_include_directories(%s SYSTEM %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.system_includes)),
|
||||
]
|
||||
)
|
||||
if info.quote_includes:
|
||||
if is_windows:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_include_directories(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.quote_includes)),
|
||||
]
|
||||
)
|
||||
else:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_compile_options(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(["-iquote%s" % i for i in info.quote_includes])),
|
||||
]
|
||||
)
|
||||
if info.copts and info.modifier != "INTERFACE":
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_compile_options(%s PRIVATE %s)" % (info.name, " ".join(info.copts)),
|
||||
]
|
||||
)
|
||||
if info.linkopts:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_link_options(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.linkopts)),
|
||||
]
|
||||
)
|
||||
if info.force_cxx_compilation and any([f.endswith(".c") for f in info.srcs]):
|
||||
commands += [
|
||||
commands.append(
|
||||
"set_source_files_properties(%s PROPERTIES LANGUAGE CXX)" % " ".join([f for f in info.srcs if f.endswith(".c")]),
|
||||
]
|
||||
)
|
||||
if info.defines:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_compile_definitions(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.defines)),
|
||||
]
|
||||
)
|
||||
if info.local_defines:
|
||||
commands += [
|
||||
commands.append(
|
||||
"target_compile_definitions(%s %s %s)" % (info.name, info.modifier or "PRIVATE", " ".join(info.local_defines)),
|
||||
]
|
||||
)
|
||||
return commands
|
||||
|
||||
GeneratedCmakeFiles = provider(
|
||||
|
||||
@@ -492,9 +492,14 @@ class NiceLocationExpr extends Expr {
|
||||
// for `import xxx` or for `import xxx as yyy`.
|
||||
this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec)
|
||||
or
|
||||
/* Show y for `y` in `from xxx import y` */
|
||||
exists(string name |
|
||||
name = this.(ImportMember).getName() and
|
||||
// Show y for `y` in `from xxx import y`
|
||||
// and y for `yyy as y` in `from xxx import yyy as y`.
|
||||
exists(string name, Alias alias |
|
||||
// This alias will always exist, as `from xxx import y`
|
||||
// is expanded to `from xxx imprt y as y`.
|
||||
this = alias.getValue() and
|
||||
name = alias.getAsname().(Name).getId()
|
||||
|
|
||||
this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and
|
||||
bl = el and
|
||||
bc = ec - name.length() + 1
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Support analyzing packages (folders with python code) that do not have `__init__.py` files, although this is technically required, we see real world projects that don't have this.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* Fixed the computation of locations for imports with aliases in jump-to-definition.
|
||||
@@ -195,7 +195,22 @@ private predicate isPotentialPackage(Folder f) {
|
||||
}
|
||||
|
||||
private string moduleNameFromBase(Container file) {
|
||||
isPotentialPackage(file) and result = file.getBaseName()
|
||||
// We used to also require `isPotentialPackage(f)` to hold in this case,
|
||||
// but we saw modules not getting resolved because their folder did not
|
||||
// contain an `__init__.py` file.
|
||||
//
|
||||
// This makes the folder not be a package but a namespace package instead.
|
||||
// In most cases this is a mistake :| See following links for more details
|
||||
// - https://dev.to/methane/don-t-omit-init-py-3hga
|
||||
// - https://packaging.python.org/en/latest/guides/packaging-namespace-packages/
|
||||
// - https://discuss.python.org/t/init-py-pep-420-and-iter-modules-confusion/9642
|
||||
//
|
||||
// It is possible that we can keep the original requirement on
|
||||
// `isPotentialPackage(f)` here, but relax `isPotentialPackage` itself to allow
|
||||
// for this behavior of missing `__init__.py` files. However, doing so involves
|
||||
// cascading changes (for example to `moduleNameFromFile`), and was a more involved
|
||||
// task than we wanted to take on.
|
||||
result = file.getBaseName()
|
||||
or
|
||||
file instanceof File and result = file.getStem()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ Since PEP 420 was accepted in Python 3, this test is Python 3 only.
|
||||
from foo.bar.a import afunc
|
||||
from foo_explicit.bar.a import explicit_afunc
|
||||
|
||||
afunc() # $ MISSING: pt,tt=afunc
|
||||
afunc() # $ pt,tt="foo/bar/a.py:afunc"
|
||||
|
||||
explicit_afunc() # $ pt,tt="foo_explicit/bar/a.py:explicit_afunc"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
from nova.api.openstack.placement import microversion
|
||||
from nova.api.openstack.placement.objects import resource_provider as rp_obj
|
||||
from nova.api.openstack.placement.policies import allocation_candidate as \
|
||||
policies
|
||||
@@ -0,0 +1,6 @@
|
||||
| import_statements.py | 1 | 6 | 1 | 33 |
|
||||
| import_statements.py | 1 | 42 | 1 | 53 |
|
||||
| import_statements.py | 2 | 6 | 2 | 41 |
|
||||
| import_statements.py | 2 | 71 | 2 | 76 |
|
||||
| import_statements.py | 3 | 6 | 3 | 42 |
|
||||
| import_statements.py | 4 | 5 | 4 | 12 |
|
||||
@@ -0,0 +1,6 @@
|
||||
import python
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
from NiceLocationExpr expr, string f, int bl, int bc, int el, int ec
|
||||
where expr.hasLocationInfo(f, bl, bc, el, ec)
|
||||
select f, bl, bc, el, ec
|
||||
@@ -35,9 +35,9 @@
|
||||
| redos.py:139:25:139:31 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
|
||||
| redos.py:145:25:145:32 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| redos.py:148:25:148:31 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
|
||||
| redos.py:151:25:151:34 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
|
||||
| redos.py:157:25:157:34 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| redos.py:151:25:151:34 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
|
||||
| redos.py:157:25:157:34 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| redos.py:160:25:160:32 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. |
|
||||
| redos.py:163:25:163:32 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| redos.py:166:25:166:34 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
@@ -67,8 +67,8 @@
|
||||
| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\tthisisagoddamnlongstringforstresstestingthequery'. |
|
||||
| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'thisisagoddamnlongstringforstresstestingthequery'. |
|
||||
| redos.py:262:78:262:80 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with 'this' and containing many repetitions of '0querythis'. |
|
||||
| redos.py:268:28:268:39 | ([\ufffd\ufffd]\|[\ufffd\ufffd])* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\ufffd'. |
|
||||
| redos.py:271:28:271:41 | ((\ufffd\|\ufffd)\|(\ufffd\|\ufffd))* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\ufffd'. |
|
||||
| redos.py:268:28:268:39 | ([\ufffd\ufffd]\|[\ufffd\ufffd])* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\\ufffd'. |
|
||||
| redos.py:271:28:271:41 | ((\ufffd\|\ufffd)\|(\ufffd\|\ufffd))* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\\ufffd'. |
|
||||
| redos.py:274:31:274:32 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. |
|
||||
| redos.py:277:48:277:50 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '<0\\t0=' and containing many repetitions of '""\\t0='. |
|
||||
| redos.py:283:26:283:27 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
|
||||
@@ -103,5 +103,5 @@
|
||||
| redos.py:385:24:385:30 | (\\d\|0)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| redos.py:386:26:386:32 | (\\d\|0)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| redos.py:391:15:391:25 | (\\u0061\|a)* | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of 'a'. |
|
||||
| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of '\u00c6'. |
|
||||
| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of '\\u00c6'. |
|
||||
| unittests.py:9:16:9:24 | (?:.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. |
|
||||
|
||||
@@ -48,7 +48,7 @@ class UnaryLogicalOperation extends UnaryOperation, TUnaryLogicalOperation { }
|
||||
* not params.empty?
|
||||
* ```
|
||||
*/
|
||||
class NotExpr extends UnaryLogicalOperation, TNotExpr {
|
||||
class NotExpr extends UnaryLogicalOperation instanceof NotExprImpl {
|
||||
final override string getAPrimaryQlClass() { result = "NotExpr" }
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class ComplementExpr extends UnaryBitwiseOperation, TComplementExpr {
|
||||
* defined? some_method
|
||||
* ```
|
||||
*/
|
||||
class DefinedExpr extends UnaryOperation, TDefinedExpr {
|
||||
class DefinedExpr extends UnaryOperation instanceof DefinedExprImpl {
|
||||
final override string getAPrimaryQlClass() { result = "DefinedExpr" }
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ private module Cached {
|
||||
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
|
||||
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
|
||||
} or
|
||||
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
|
||||
TDefinedExprReal(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
|
||||
TDefinedExprSynth(Ast::AstNode parent, int i) { mkSynthChild(DefinedExprKind(), parent, i) } or
|
||||
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
|
||||
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
|
||||
not strictcount(int i | exists(g.getParent().(Ruby::LeftAssignmentList).getChild(i))) = 1
|
||||
@@ -228,7 +229,8 @@ private module Cached {
|
||||
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
|
||||
TNotExprReal(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or
|
||||
TNotExprSynth(Ast::AstNode parent, int i) { mkSynthChild(NotExprKind(), parent, i) } or
|
||||
TOptionalParameter(Ruby::OptionalParameter g) or
|
||||
TPair(Ruby::Pair g) or
|
||||
TParenthesizedExpr(Ruby::ParenthesizedStatements g) or
|
||||
@@ -354,21 +356,21 @@ private module Cached {
|
||||
TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or TBlockParameter 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
|
||||
TElseReal or TElsif or TEmptyStmt or TEncoding or TEndBlock or TEnsure or TEqExpr or
|
||||
TExponentExprReal or 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
|
||||
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
|
||||
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
|
||||
TComplexLiteral or TDefinedExprReal or TDelimitedSymbolLiteral or
|
||||
TDestructuredLeftAssignment or TDestructuredParameter or TDivExprReal or TDo or TDoBlock or
|
||||
TElementReference or TElseReal or TElsif or TEmptyStmt or TEncoding or TEndBlock or
|
||||
TEnsure or TEqExpr or TExponentExprReal or 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 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 TModuleDeclaration or TModuloExprReal or TMulExprReal or TNEExpr or
|
||||
TNextStmt or TNilLiteralReal or TNoRegExpMatchExpr or TNotExprReal 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
|
||||
@@ -438,7 +440,7 @@ private module Cached {
|
||||
n = TClassVariableAccessReal(result, _) or
|
||||
n = TComplementExpr(result) or
|
||||
n = TComplexLiteral(result) or
|
||||
n = TDefinedExpr(result) or
|
||||
n = TDefinedExprReal(result) or
|
||||
n = TDelimitedSymbolLiteral(result) or
|
||||
n = TDestructuredLeftAssignment(result) or
|
||||
n = TDivExprReal(result) or
|
||||
@@ -495,7 +497,7 @@ private module Cached {
|
||||
n = TNextStmt(result) or
|
||||
n = TNilLiteralReal(result) or
|
||||
n = TNoRegExpMatchExpr(result) or
|
||||
n = TNotExpr(result) or
|
||||
n = TNotExprReal(result) or
|
||||
n = TOptionalParameter(result) or
|
||||
n = TPair(result) or
|
||||
n = TParenthesizedExpr(result) or
|
||||
@@ -585,6 +587,8 @@ private module Cached {
|
||||
or
|
||||
result = TConstantWriteAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TDefinedExprSynth(parent, i)
|
||||
or
|
||||
result = TDivExprSynth(parent, i)
|
||||
or
|
||||
result = TElseSynth(parent, i)
|
||||
@@ -617,6 +621,8 @@ private module Cached {
|
||||
or
|
||||
result = TNilLiteralSynth(parent, i)
|
||||
or
|
||||
result = TNotExprSynth(parent, i)
|
||||
or
|
||||
result = TRangeLiteralSynth(parent, i, _)
|
||||
or
|
||||
result = TRShiftExprSynth(parent, i)
|
||||
@@ -789,10 +795,14 @@ class TNamespace = TClassDeclaration or TModuleDeclaration;
|
||||
|
||||
class TOperation = TUnaryOperation or TBinaryOperation or TAssignment;
|
||||
|
||||
class TDefinedExpr = TDefinedExprReal or TDefinedExprSynth;
|
||||
|
||||
class TUnaryOperation =
|
||||
TUnaryLogicalOperation or TUnaryArithmeticOperation or TUnaryBitwiseOperation or TDefinedExpr or
|
||||
TSplatExpr or THashSplatExpr;
|
||||
|
||||
class TNotExpr = TNotExprReal or TNotExprSynth;
|
||||
|
||||
class TUnaryLogicalOperation = TNotExpr;
|
||||
|
||||
class TUnaryArithmeticOperation = TUnaryPlusExpr or TUnaryMinusExpr;
|
||||
|
||||
@@ -35,6 +35,16 @@ class UnaryOperationGenerated extends UnaryOperationImpl {
|
||||
final override string getOperatorImpl() { result = g.getOperator() }
|
||||
}
|
||||
|
||||
abstract class NotExprImpl extends UnaryOperationImpl, TNotExpr { }
|
||||
|
||||
class NotExprReal extends NotExprImpl, UnaryOperationGenerated, TNotExprReal { }
|
||||
|
||||
class NotExprSynth extends NotExprImpl, TNotExprSynth {
|
||||
final override string getOperatorImpl() { result = "!" }
|
||||
|
||||
final override Expr getOperandImpl() { synthChild(this, 0, result) }
|
||||
}
|
||||
|
||||
class SplatExprReal extends UnaryOperationImpl, TSplatExprReal {
|
||||
private Ruby::SplatArgument g;
|
||||
|
||||
@@ -67,6 +77,16 @@ class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr {
|
||||
final override string getOperatorImpl() { result = "**" }
|
||||
}
|
||||
|
||||
abstract class DefinedExprImpl extends UnaryOperationImpl, TDefinedExpr { }
|
||||
|
||||
class DefinedExprReal extends DefinedExprImpl, UnaryOperationGenerated, TDefinedExprReal { }
|
||||
|
||||
class DefinedExprSynth extends DefinedExprImpl, TDefinedExprSynth {
|
||||
final override string getOperatorImpl() { result = "defined?" }
|
||||
|
||||
final override Expr getOperandImpl() { synthChild(this, 0, result) }
|
||||
}
|
||||
|
||||
abstract class BinaryOperationImpl extends OperationImpl, MethodCallImpl, TBinaryOperation {
|
||||
abstract Stmt getLeftOperandImpl();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ newtype SynthKind =
|
||||
BraceBlockKind() or
|
||||
CaseMatchKind() or
|
||||
ClassVariableAccessKind(ClassVariable v) or
|
||||
DefinedExprKind() or
|
||||
DivExprKind() or
|
||||
ElseKind() or
|
||||
ExponentExprKind() or
|
||||
@@ -40,6 +41,7 @@ newtype SynthKind =
|
||||
ModuloExprKind() or
|
||||
MulExprKind() or
|
||||
NilLiteralKind() or
|
||||
NotExprKind() or
|
||||
RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or
|
||||
RShiftExprKind() or
|
||||
SimpleParameterKind() or
|
||||
@@ -1258,6 +1260,7 @@ private module HashLiteralDesugar {
|
||||
* ```
|
||||
* desugars to, roughly,
|
||||
* ```rb
|
||||
* if not defined? x then x = nil end
|
||||
* xs.each { |__synth__0| x = __synth__0; <loop_body> }
|
||||
* ```
|
||||
*
|
||||
@@ -1267,58 +1270,160 @@ private module HashLiteralDesugar {
|
||||
* scoped to the synthesized block.
|
||||
*/
|
||||
private module ForLoopDesugar {
|
||||
private Ruby::AstNode getForLoopPatternChild(Ruby::For for) {
|
||||
result = for.getPattern()
|
||||
or
|
||||
result.getParent() = getForLoopPatternChild(for)
|
||||
}
|
||||
|
||||
/** Holds if `n` is an access to variable `v` in the pattern of `for`. */
|
||||
pragma[nomagic]
|
||||
private predicate forLoopVariableAccess(Ruby::For for, Ruby::AstNode n, VariableReal v) {
|
||||
n = getForLoopPatternChild(for) and
|
||||
access(n, v)
|
||||
}
|
||||
|
||||
/** Holds if `v` is the `i`th iteration variable of `for`. */
|
||||
private predicate forLoopVariable(Ruby::For for, VariableReal v, int i) {
|
||||
v =
|
||||
rank[i + 1](VariableReal v0, Ruby::AstNode n, Location l |
|
||||
forLoopVariableAccess(for, n, v0) and
|
||||
l = n.getLocation()
|
||||
|
|
||||
v0 order by l.getStartLine(), l.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number of iteration variables of `for`. */
|
||||
private int forLoopVariableCount(Ruby::For for) {
|
||||
result = count(int j | forLoopVariable(for, _, j))
|
||||
}
|
||||
|
||||
private Ruby::For toTsFor(ForExpr for) { for = TForExpr(result) }
|
||||
|
||||
/**
|
||||
* Synthesizes an assignment
|
||||
* ```rb
|
||||
* if not defined? v then v = nil end
|
||||
* ```
|
||||
* anchored at index `rootIndex` of `root`.
|
||||
*/
|
||||
bindingset[root, rootIndex, v]
|
||||
private predicate nilAssignUndefined(
|
||||
AstNode root, int rootIndex, AstNode parent, int i, Child child, VariableReal v
|
||||
) {
|
||||
parent = root and
|
||||
i = rootIndex and
|
||||
child = SynthChild(IfKind())
|
||||
or
|
||||
exists(AstNode if_ | if_ = TIfSynth(root, rootIndex) |
|
||||
parent = if_ and
|
||||
i = 0 and
|
||||
child = SynthChild(NotExprKind())
|
||||
or
|
||||
exists(AstNode not_ | not_ = TNotExprSynth(if_, 0) |
|
||||
parent = not_ and
|
||||
i = 0 and
|
||||
child = SynthChild(DefinedExprKind())
|
||||
or
|
||||
parent = TDefinedExprSynth(not_, 0) and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessRealKind(v))
|
||||
)
|
||||
or
|
||||
parent = if_ and
|
||||
i = 1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
parent = TAssignExprSynth(if_, 1) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessRealKind(v))
|
||||
or
|
||||
i = 1 and
|
||||
child = SynthChild(NilLiteralKind())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate forLoopSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(ForExpr for |
|
||||
// each call
|
||||
parent = for and
|
||||
i = -1 and
|
||||
child = SynthChild(MethodCallKind("each", false, 0))
|
||||
child = SynthChild(StmtSequenceKind())
|
||||
or
|
||||
exists(MethodCall eachCall | eachCall = TMethodCallSynth(for, -1, "each", false, 0) |
|
||||
// receiver
|
||||
parent = eachCall and
|
||||
i = 0 and
|
||||
child = childRef(for.getValue()) // value is the Enumerable
|
||||
exists(AstNode seq | seq = TStmtSequenceSynth(for, -1) |
|
||||
exists(VariableReal v, int j | forLoopVariable(toTsFor(for), v, j) |
|
||||
nilAssignUndefined(seq, j, parent, i, child, v)
|
||||
)
|
||||
or
|
||||
parent = eachCall and
|
||||
i = 1 and
|
||||
child = SynthChild(BraceBlockKind())
|
||||
or
|
||||
exists(Block block | block = TBraceBlockSynth(eachCall, 1) |
|
||||
// block params
|
||||
parent = block and
|
||||
i = 0 and
|
||||
child = SynthChild(SimpleParameterKind())
|
||||
exists(int numberOfVars | numberOfVars = forLoopVariableCount(toTsFor(for)) |
|
||||
// each call
|
||||
parent = seq and
|
||||
i = numberOfVars and
|
||||
child = SynthChild(MethodCallKind("each", false, 0))
|
||||
or
|
||||
exists(SimpleParameter param | param = TSimpleParameterSynth(block, 0) |
|
||||
parent = param and
|
||||
exists(MethodCall eachCall |
|
||||
eachCall = TMethodCallSynth(seq, numberOfVars, "each", false, 0)
|
||||
|
|
||||
// receiver
|
||||
parent = eachCall and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
|
||||
child = childRef(for.getValue()) // value is the Enumerable
|
||||
or
|
||||
// assignment to pattern from for loop to synth parameter
|
||||
parent = block and
|
||||
parent = eachCall and
|
||||
i = 1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
child = SynthChild(BraceBlockKind())
|
||||
or
|
||||
parent = TAssignExprSynth(block, 1) and
|
||||
(
|
||||
exists(Block block | block = TBraceBlockSynth(eachCall, 1) |
|
||||
// block params
|
||||
parent = block and
|
||||
i = 0 and
|
||||
child = childRef(for.getPattern())
|
||||
child = SynthChild(SimpleParameterKind())
|
||||
or
|
||||
i = 1 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
|
||||
exists(SimpleParameter param | param = TSimpleParameterSynth(block, 0) |
|
||||
parent = param and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
|
||||
or
|
||||
// assignment to pattern from for loop to synth parameter
|
||||
parent = block and
|
||||
i = 1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
parent = TAssignExprSynth(block, 1) and
|
||||
(
|
||||
i = 0 and
|
||||
child = childRef(for.getPattern())
|
||||
or
|
||||
i = 1 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
|
||||
)
|
||||
)
|
||||
or
|
||||
// rest of block body
|
||||
parent = block and
|
||||
child = childRef(for.getBody().(Do).getStmt(i - 2))
|
||||
)
|
||||
)
|
||||
or
|
||||
// rest of block body
|
||||
parent = block and
|
||||
child = childRef(for.getBody().(Do).getStmt(i - 2))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isDesugaredInitNode(ForExpr for, Variable v, AstNode n) {
|
||||
exists(StmtSequence seq, AssignExpr ae |
|
||||
seq = for.getDesugared() and
|
||||
n = seq.getStmt(_) and
|
||||
ae = n.(IfExpr).getThen() and
|
||||
v = ae.getLeftOperand().getAVariable()
|
||||
)
|
||||
or
|
||||
isDesugaredInitNode(for, v, n.getParent())
|
||||
}
|
||||
|
||||
private class ForLoopSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
forLoopSynthesis(parent, i, child)
|
||||
@@ -1338,6 +1443,14 @@ private module ForLoopDesugar {
|
||||
final override predicate excludeFromControlFlowTree(AstNode n) {
|
||||
n = any(ForExpr for).getBody()
|
||||
}
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(ForExpr for, Ruby::AstNode access, Variable v |
|
||||
forLoopVariableAccess(toTsFor(for), access, v) and
|
||||
isDesugaredInitNode(for, v, n) and
|
||||
l = access.getLocation()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,22 +137,6 @@ module LocalFlow {
|
||||
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNodeImpl).getMethod())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
|
||||
* that parameter.
|
||||
*
|
||||
* This is intended to recover from flow not currently recognised by ordinary capture flow.
|
||||
*/
|
||||
predicate localFlowSsaParamCaptureInput(ParameterNodeImpl nodeFrom, Node nodeTo) {
|
||||
exists(Ssa::CapturedEntryDefinition def |
|
||||
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
|
||||
|
|
||||
nodeFrom.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
|
||||
or
|
||||
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
|
||||
* involving SSA definition `def`.
|
||||
@@ -285,6 +269,53 @@ predicate isNonConstantExpr(CfgNodes::ExprCfgNode n) {
|
||||
not n.getExpr() instanceof ConstantAccess
|
||||
}
|
||||
|
||||
/** Provides logic related to captured variables. */
|
||||
module VariableCapture {
|
||||
class CapturedVariable extends LocalVariable {
|
||||
CapturedVariable() { this.isCaptured() }
|
||||
|
||||
CfgScope getCfgScope() {
|
||||
exists(Scope scope | scope = this.getDeclaringScope() |
|
||||
result = scope
|
||||
or
|
||||
result = scope.(ModuleBase).getCfgScope()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt {
|
||||
CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is control-flow insensitive data-flow from `node1` to `node2`
|
||||
* involving a captured variable. Only used in type tracking.
|
||||
*/
|
||||
predicate flowInsensitiveStep(Node node1, Node node2) {
|
||||
exists(CapturedSsaDefinitionExt def, CapturedVariable v |
|
||||
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node
|
||||
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
|
||||
def.getSourceVariable() = v and
|
||||
(
|
||||
def instanceof Ssa::WriteDefinition
|
||||
or
|
||||
def instanceof Ssa::SelfDefinition
|
||||
) and
|
||||
node2.(CapturedVariableNode).getVariable() = v
|
||||
or
|
||||
// From a captured variable node to its flow-sensitive capture nodes
|
||||
node1.(CapturedVariableNode).getVariable() = v and
|
||||
def = node2.(SsaDefinitionExtNode).getDefinitionExt() and
|
||||
def.getSourceVariable() = v and
|
||||
(
|
||||
def instanceof Ssa::CapturedCallDefinition
|
||||
or
|
||||
def instanceof Ssa::CapturedEntryDefinition
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A collection of cached types and predicates to be evaluated in the same stage. */
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -296,6 +327,7 @@ private module Cached {
|
||||
TExprNode(CfgNodes::ExprCfgNode n) { TaintTrackingPrivate::forceCachingInSameStage() } or
|
||||
TReturningNode(CfgNodes::ReturningCfgNode n) or
|
||||
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
|
||||
TCapturedVariableNode(VariableCapture::CapturedVariable v) or
|
||||
TNormalParameterNode(Parameter p) {
|
||||
p instanceof SimpleParameter or
|
||||
p instanceof OptionalParameter or
|
||||
@@ -389,6 +421,8 @@ private module Cached {
|
||||
LocalFlow::localFlowSsaInputFromRead(exprFrom, _, nodeTo) and
|
||||
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()]
|
||||
)
|
||||
or
|
||||
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
|
||||
@@ -550,7 +584,7 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
|
||||
}
|
||||
|
||||
/** An SSA definition for a `self` variable. */
|
||||
class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
|
||||
class SsaSelfDefinitionNode extends SsaDefinitionExtNode {
|
||||
private SelfVariable self;
|
||||
|
||||
SsaSelfDefinitionNode() { self = def.getSourceVariable() }
|
||||
@@ -559,6 +593,22 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
|
||||
Scope getSelfScope() { result = self.getDeclaringScope() }
|
||||
}
|
||||
|
||||
/** A data flow node representing a captured variable. Only used in type tracking. */
|
||||
class CapturedVariableNode extends NodeImpl, TCapturedVariableNode {
|
||||
private VariableCapture::CapturedVariable variable;
|
||||
|
||||
CapturedVariableNode() { this = TCapturedVariableNode(variable) }
|
||||
|
||||
/** Gets the captured variable represented by this node. */
|
||||
VariableCapture::CapturedVariable getVariable() { result = variable }
|
||||
|
||||
override CfgScope getCfgScope() { result = variable.getCfgScope() }
|
||||
|
||||
override Location getLocationImpl() { result = variable.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = "captured " + variable.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value returning statement, viewed as a node in a data flow graph.
|
||||
*
|
||||
@@ -1163,13 +1213,7 @@ private module OutNodes {
|
||||
|
||||
import OutNodes
|
||||
|
||||
predicate jumpStep(Node pred, Node succ) {
|
||||
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
|
||||
succ.(SsaDefinitionExtNode).getDefinitionExt())
|
||||
or
|
||||
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
|
||||
succ.(SsaDefinitionExtNode).getDefinitionExt())
|
||||
or
|
||||
predicate jumpStepTypeTracker(Node pred, Node succ) {
|
||||
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
|
||||
@@ -1178,6 +1222,16 @@ predicate jumpStep(Node pred, Node succ) {
|
||||
any(AdditionalJumpStep s).step(pred, succ)
|
||||
}
|
||||
|
||||
predicate jumpStep(Node pred, Node succ) {
|
||||
jumpStepTypeTracker(pred, succ)
|
||||
or
|
||||
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
|
||||
succ.(SsaDefinitionExtNode).getDefinitionExt())
|
||||
or
|
||||
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
|
||||
succ.(SsaDefinitionExtNode).getDefinitionExt())
|
||||
}
|
||||
|
||||
private ContentSet getKeywordContent(string name) {
|
||||
exists(ConstantValue::ConstantSymbolValue key |
|
||||
result.isSingleton(TKnownElementContent(key)) and
|
||||
|
||||
@@ -363,10 +363,9 @@ private module Cached {
|
||||
source = sink and
|
||||
source instanceof LocalSourceNode
|
||||
or
|
||||
exists(Node mid | hasLocalSource(mid, source) |
|
||||
exists(Node mid |
|
||||
hasLocalSource(mid, source) and
|
||||
localFlowStepTypeTracker(mid, sink)
|
||||
or
|
||||
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2;
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
|
||||
*/
|
||||
predicate jumpStep = DataFlowPrivate::jumpStep/2;
|
||||
predicate jumpStep = DataFlowPrivate::jumpStepTypeTracker/2;
|
||||
|
||||
/** Holds if there is direct flow from `param` to a return. */
|
||||
pragma[nomagic]
|
||||
|
||||
@@ -9,14 +9,45 @@
|
||||
|
||||
private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::TestOutput
|
||||
private import codeql.IDEContextual
|
||||
private import codeql.Locations
|
||||
private import codeql.ruby.controlflow.ControlFlowGraph
|
||||
|
||||
/**
|
||||
* Gets the source file to generate a CFG from.
|
||||
*/
|
||||
external string selectedSourceFile();
|
||||
|
||||
external string selectedSourceLine();
|
||||
|
||||
external string selectedSourceColumn();
|
||||
|
||||
bindingset[file, line, column]
|
||||
private CfgScope smallestEnclosingScope(File file, int line, int column) {
|
||||
result =
|
||||
min(Location loc, CfgScope scope |
|
||||
loc = scope.getLocation() and
|
||||
(
|
||||
loc.getStartLine() < line
|
||||
or
|
||||
loc.getStartLine() = line and loc.getStartColumn() <= column
|
||||
) and
|
||||
(
|
||||
loc.getEndLine() > line
|
||||
or
|
||||
loc.getEndLine() = line and loc.getEndColumn() >= column
|
||||
) and
|
||||
loc.getFile() = file
|
||||
|
|
||||
scope
|
||||
order by
|
||||
loc.getStartLine() desc, loc.getStartColumn() desc, loc.getEndLine(), loc.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
class MyRelevantNode extends RelevantNode {
|
||||
MyRelevantNode() {
|
||||
this.getScope().getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
this.getScope() =
|
||||
smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()),
|
||||
selectedSourceLine().toInt(), selectedSourceColumn().toInt())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,29 +24,45 @@ calls/calls.rb:
|
||||
# 67| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar
|
||||
# 67| getReceiver: [ConstantReadAccess] X
|
||||
# 226| [ForExpr] for ... in ...
|
||||
# 226| getDesugared: [MethodCall] call to each
|
||||
# 226| getReceiver: [MethodCall] call to bar
|
||||
# 226| getReceiver: [SelfVariableAccess] self
|
||||
# 226| getBlock: [BraceBlock] { ... }
|
||||
# 226| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 226| getStmt: [AssignExpr] ... = ...
|
||||
# 226| getDesugared: [StmtSequence] ...
|
||||
# 226| getStmt: [IfExpr] if ...
|
||||
# 226| getCondition: [NotExpr] ! ...
|
||||
# 226| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 226| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
|
||||
# 226| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 227| getStmt: [MethodCall] call to baz
|
||||
# 227| getReceiver: [SelfVariableAccess] self
|
||||
# 226| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 226| getStmt: [MethodCall] call to each
|
||||
# 226| getReceiver: [MethodCall] call to bar
|
||||
# 226| getReceiver: [SelfVariableAccess] self
|
||||
# 226| getBlock: [BraceBlock] { ... }
|
||||
# 226| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 226| getStmt: [AssignExpr] ... = ...
|
||||
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 227| getStmt: [MethodCall] call to baz
|
||||
# 227| getReceiver: [SelfVariableAccess] self
|
||||
# 229| [ForExpr] for ... in ...
|
||||
# 229| getDesugared: [MethodCall] call to each
|
||||
# 229| getReceiver: [MethodCall] call to bar
|
||||
# 229| getReceiver: [ConstantReadAccess] X
|
||||
# 229| getBlock: [BraceBlock] { ... }
|
||||
# 229| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 229| getStmt: [AssignExpr] ... = ...
|
||||
# 229| getDesugared: [StmtSequence] ...
|
||||
# 229| getStmt: [IfExpr] if ...
|
||||
# 229| getCondition: [NotExpr] ! ...
|
||||
# 229| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 229| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
|
||||
# 229| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 230| getStmt: [MethodCall] call to baz
|
||||
# 230| getReceiver: [ConstantReadAccess] X
|
||||
# 229| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 229| getStmt: [MethodCall] call to each
|
||||
# 229| getReceiver: [MethodCall] call to bar
|
||||
# 229| getReceiver: [ConstantReadAccess] X
|
||||
# 229| getBlock: [BraceBlock] { ... }
|
||||
# 229| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 229| getStmt: [AssignExpr] ... = ...
|
||||
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 230| getStmt: [MethodCall] call to baz
|
||||
# 230| getReceiver: [ConstantReadAccess] X
|
||||
# 249| [HashLiteral] {...}
|
||||
# 249| getDesugared: [MethodCall] call to []
|
||||
# 249| getReceiver: [ConstantReadAccess] Hash
|
||||
@@ -245,52 +261,74 @@ calls/calls.rb:
|
||||
# 322| getArgument: [LocalVariableAccess] __synth__4
|
||||
# 322| getStmt: [LocalVariableAccess] __synth__4
|
||||
# 342| [ForExpr] for ... in ...
|
||||
# 342| getDesugared: [MethodCall] call to each
|
||||
# 342| getReceiver: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [IntegerLiteral] 1
|
||||
# 342| getArgument: [IntegerLiteral] 2
|
||||
# 342| getArgument: [IntegerLiteral] 3
|
||||
# 342| getArgument: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [IntegerLiteral] 4
|
||||
# 342| getArgument: [IntegerLiteral] 5
|
||||
# 342| getArgument: [IntegerLiteral] 6
|
||||
# 342| getBlock: [BraceBlock] { ... }
|
||||
# 342| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 342| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getDesugared: [StmtSequence] ...
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getArgument: [IntegerLiteral] 0
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getDesugared: [StmtSequence] ...
|
||||
# 342| getStmt: [IfExpr] if ...
|
||||
# 342| getCondition: [NotExpr] ! ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
|
||||
# 342| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 342| getStmt: [IfExpr] if ...
|
||||
# 342| getCondition: [NotExpr] ! ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] y
|
||||
# 342| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
|
||||
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 342| getStmt: [IfExpr] if ...
|
||||
# 342| getCondition: [NotExpr] ! ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] z
|
||||
# 342| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
|
||||
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 342| getStmt: [MethodCall] call to each
|
||||
# 342| getReceiver: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [IntegerLiteral] 1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getArgument: [IntegerLiteral] 2
|
||||
# 342| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 343| getStmt: [MethodCall] call to foo
|
||||
# 343| getReceiver: [SelfVariableAccess] self
|
||||
# 343| getArgument: [LocalVariableAccess] x
|
||||
# 343| getArgument: [LocalVariableAccess] y
|
||||
# 343| getArgument: [LocalVariableAccess] z
|
||||
# 342| getArgument: [IntegerLiteral] 3
|
||||
# 342| getArgument: [ArrayLiteral] [...]
|
||||
# 342| getDesugared: [MethodCall] call to []
|
||||
# 342| getReceiver: [ConstantReadAccess] Array
|
||||
# 342| getArgument: [IntegerLiteral] 4
|
||||
# 342| getArgument: [IntegerLiteral] 5
|
||||
# 342| getArgument: [IntegerLiteral] 6
|
||||
# 342| getBlock: [BraceBlock] { ... }
|
||||
# 342| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 342| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getDesugared: [StmtSequence] ...
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getArgument: [IntegerLiteral] 0
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getArgument: [IntegerLiteral] 1
|
||||
# 342| getStmt: [AssignExpr] ... = ...
|
||||
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
|
||||
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
|
||||
# 342| getArgument: [IntegerLiteral] 2
|
||||
# 342| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 343| getStmt: [MethodCall] call to foo
|
||||
# 343| getReceiver: [SelfVariableAccess] self
|
||||
# 343| getArgument: [LocalVariableAccess] x
|
||||
# 343| getArgument: [LocalVariableAccess] y
|
||||
# 343| getArgument: [LocalVariableAccess] z
|
||||
# 364| [MethodCall] call to empty?
|
||||
# 364| getDesugared: [StmtSequence] ...
|
||||
# 364| getStmt: [AssignExpr] ... = ...
|
||||
@@ -594,139 +632,185 @@ literals/literals.rb:
|
||||
# 199| getValue: [ConstantReadAccess] Z
|
||||
control/loops.rb:
|
||||
# 9| [ForExpr] for ... in ...
|
||||
# 9| getDesugared: [MethodCall] call to each
|
||||
# 9| getReceiver: [RangeLiteral] _ .. _
|
||||
# 9| getBegin: [IntegerLiteral] 1
|
||||
# 9| getEnd: [IntegerLiteral] 10
|
||||
# 9| getBlock: [BraceBlock] { ... }
|
||||
# 9| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 9| getStmt: [AssignExpr] ... = ...
|
||||
# 9| getDesugared: [StmtSequence] ...
|
||||
# 9| getStmt: [IfExpr] if ...
|
||||
# 9| getCondition: [NotExpr] ! ...
|
||||
# 9| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 9| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] n
|
||||
# 9| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
|
||||
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 10| getStmt: [AssignAddExpr] ... += ...
|
||||
# 10| getDesugared: [AssignExpr] ... = ...
|
||||
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 11| getStmt: [AssignExpr] ... = ...
|
||||
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
|
||||
# 9| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 9| getStmt: [MethodCall] call to each
|
||||
# 9| getReceiver: [RangeLiteral] _ .. _
|
||||
# 9| getBegin: [IntegerLiteral] 1
|
||||
# 9| getEnd: [IntegerLiteral] 10
|
||||
# 9| getBlock: [BraceBlock] { ... }
|
||||
# 9| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 9| getStmt: [AssignExpr] ... = ...
|
||||
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
|
||||
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 10| getStmt: [AssignAddExpr] ... += ...
|
||||
# 10| getDesugared: [AssignExpr] ... = ...
|
||||
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 11| getStmt: [AssignExpr] ... = ...
|
||||
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
|
||||
# 16| [ForExpr] for ... in ...
|
||||
# 16| getDesugared: [MethodCall] call to each
|
||||
# 16| getReceiver: [RangeLiteral] _ .. _
|
||||
# 16| getBegin: [IntegerLiteral] 1
|
||||
# 16| getEnd: [IntegerLiteral] 10
|
||||
# 16| getBlock: [BraceBlock] { ... }
|
||||
# 16| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 16| getStmt: [AssignExpr] ... = ...
|
||||
# 16| getDesugared: [StmtSequence] ...
|
||||
# 16| getStmt: [IfExpr] if ...
|
||||
# 16| getCondition: [NotExpr] ! ...
|
||||
# 16| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 16| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] n
|
||||
# 16| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
|
||||
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 17| getStmt: [AssignAddExpr] ... += ...
|
||||
# 17| getDesugared: [AssignExpr] ... = ...
|
||||
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 18| getStmt: [AssignSubExpr] ... -= ...
|
||||
# 18| getDesugared: [AssignExpr] ... = ...
|
||||
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
|
||||
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 16| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 16| getStmt: [MethodCall] call to each
|
||||
# 16| getReceiver: [RangeLiteral] _ .. _
|
||||
# 16| getBegin: [IntegerLiteral] 1
|
||||
# 16| getEnd: [IntegerLiteral] 10
|
||||
# 16| getBlock: [BraceBlock] { ... }
|
||||
# 16| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 16| getStmt: [AssignExpr] ... = ...
|
||||
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
|
||||
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 17| getStmt: [AssignAddExpr] ... += ...
|
||||
# 17| getDesugared: [AssignExpr] ... = ...
|
||||
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 18| getStmt: [AssignSubExpr] ... -= ...
|
||||
# 18| getDesugared: [AssignExpr] ... = ...
|
||||
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
|
||||
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
|
||||
# 22| [ForExpr] for ... in ...
|
||||
# 22| getDesugared: [MethodCall] call to each
|
||||
# 22| getReceiver: [HashLiteral] {...}
|
||||
# 22| getDesugared: [MethodCall] call to []
|
||||
# 22| getReceiver: [ConstantReadAccess] Hash
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getComponent: [StringTextComponent] foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getComponent: [StringTextComponent] bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 22| getBlock: [BraceBlock] { ... }
|
||||
# 22| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getDesugared: [StmtSequence] ...
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getArgument: [IntegerLiteral] 0
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getArgument: [IntegerLiteral] 1
|
||||
# 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 23| getStmt: [AssignAddExpr] ... += ...
|
||||
# 23| getDesugared: [AssignExpr] ... = ...
|
||||
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 24| getStmt: [AssignMulExpr] ... *= ...
|
||||
# 24| getDesugared: [AssignExpr] ... = ...
|
||||
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
|
||||
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 22| getDesugared: [StmtSequence] ...
|
||||
# 22| getStmt: [IfExpr] if ...
|
||||
# 22| getCondition: [NotExpr] ! ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] key
|
||||
# 22| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 22| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 22| getStmt: [IfExpr] if ...
|
||||
# 22| getCondition: [NotExpr] ! ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] value
|
||||
# 22| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 22| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 22| getStmt: [MethodCall] call to each
|
||||
# 22| getReceiver: [HashLiteral] {...}
|
||||
# 22| getDesugared: [MethodCall] call to []
|
||||
# 22| getReceiver: [ConstantReadAccess] Hash
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getComponent: [StringTextComponent] foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getComponent: [StringTextComponent] bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 22| getBlock: [BraceBlock] { ... }
|
||||
# 22| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getDesugared: [StmtSequence] ...
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getArgument: [IntegerLiteral] 0
|
||||
# 22| getStmt: [AssignExpr] ... = ...
|
||||
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 22| getArgument: [IntegerLiteral] 1
|
||||
# 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 23| getStmt: [AssignAddExpr] ... += ...
|
||||
# 23| getDesugared: [AssignExpr] ... = ...
|
||||
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 24| getStmt: [AssignMulExpr] ... *= ...
|
||||
# 24| getDesugared: [AssignExpr] ... = ...
|
||||
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
|
||||
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 28| [ForExpr] for ... in ...
|
||||
# 28| getDesugared: [MethodCall] call to each
|
||||
# 28| getReceiver: [HashLiteral] {...}
|
||||
# 28| getDesugared: [MethodCall] call to []
|
||||
# 28| getReceiver: [ConstantReadAccess] Hash
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getComponent: [StringTextComponent] foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getComponent: [StringTextComponent] bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 28| getBlock: [BraceBlock] { ... }
|
||||
# 28| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getDesugared: [StmtSequence] ...
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getArgument: [IntegerLiteral] 0
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getArgument: [IntegerLiteral] 1
|
||||
# 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 29| getStmt: [AssignAddExpr] ... += ...
|
||||
# 29| getDesugared: [AssignExpr] ... = ...
|
||||
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 30| getStmt: [AssignDivExpr] ... /= ...
|
||||
# 30| getDesugared: [AssignExpr] ... = ...
|
||||
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
|
||||
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 31| getStmt: [BreakStmt] break
|
||||
# 28| getDesugared: [StmtSequence] ...
|
||||
# 28| getStmt: [IfExpr] if ...
|
||||
# 28| getCondition: [NotExpr] ! ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] key
|
||||
# 28| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 28| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 28| getStmt: [IfExpr] if ...
|
||||
# 28| getCondition: [NotExpr] ! ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] value
|
||||
# 28| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 28| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 28| getStmt: [MethodCall] call to each
|
||||
# 28| getReceiver: [HashLiteral] {...}
|
||||
# 28| getDesugared: [MethodCall] call to []
|
||||
# 28| getReceiver: [ConstantReadAccess] Hash
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getComponent: [StringTextComponent] foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getComponent: [StringTextComponent] bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 28| getBlock: [BraceBlock] { ... }
|
||||
# 28| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getDesugared: [StmtSequence] ...
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getAnOperand/getRightOperand: [SplatExpr] * ...
|
||||
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
|
||||
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getArgument: [IntegerLiteral] 0
|
||||
# 28| getStmt: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
|
||||
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
|
||||
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
|
||||
# 28| getArgument: [IntegerLiteral] 1
|
||||
# 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
|
||||
# 29| getStmt: [AssignAddExpr] ... += ...
|
||||
# 29| getDesugared: [AssignExpr] ... = ...
|
||||
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
|
||||
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
|
||||
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 30| getStmt: [AssignDivExpr] ... /= ...
|
||||
# 30| getDesugared: [AssignExpr] ... = ...
|
||||
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
|
||||
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
|
||||
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 31| getStmt: [BreakStmt] break
|
||||
# 36| [AssignAddExpr] ... += ...
|
||||
# 36| getDesugared: [AssignExpr] ... = ...
|
||||
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
@@ -985,29 +1069,37 @@ params/params.rb:
|
||||
# 21| getReceiver: [ConstantReadAccess] Array
|
||||
erb/template.html.erb:
|
||||
# 27| [ForExpr] for ... in ...
|
||||
# 27| getDesugared: [MethodCall] call to each
|
||||
# 27| getReceiver: [ArrayLiteral] [...]
|
||||
# 27| getDesugared: [MethodCall] call to []
|
||||
# 27| getReceiver: [ConstantReadAccess] Array
|
||||
# 27| getArgument: [StringLiteral] "foo"
|
||||
# 27| getComponent: [StringTextComponent] foo
|
||||
# 27| getArgument: [StringLiteral] "bar"
|
||||
# 27| getComponent: [StringTextComponent] bar
|
||||
# 27| getArgument: [StringLiteral] "baz"
|
||||
# 27| getComponent: [StringTextComponent] baz
|
||||
# 27| getBlock: [BraceBlock] { ... }
|
||||
# 27| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 27| getStmt: [AssignExpr] ... = ...
|
||||
# 27| getDesugared: [StmtSequence] ...
|
||||
# 27| getStmt: [IfExpr] if ...
|
||||
# 27| getCondition: [NotExpr] ! ...
|
||||
# 27| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
|
||||
# 27| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
|
||||
# 27| getBranch/getThen: [AssignExpr] ... = ...
|
||||
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignAddExpr] ... += ...
|
||||
# 28| getDesugared: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
|
||||
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
|
||||
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
|
||||
# 29| getStmt: [LocalVariableAccess] xs
|
||||
# 27| getAnOperand/getRightOperand: [NilLiteral] nil
|
||||
# 27| getStmt: [MethodCall] call to each
|
||||
# 27| getReceiver: [ArrayLiteral] [...]
|
||||
# 27| getDesugared: [MethodCall] call to []
|
||||
# 27| getReceiver: [ConstantReadAccess] Array
|
||||
# 27| getArgument: [StringLiteral] "foo"
|
||||
# 27| getComponent: [StringTextComponent] foo
|
||||
# 27| getArgument: [StringLiteral] "bar"
|
||||
# 27| getComponent: [StringTextComponent] bar
|
||||
# 27| getArgument: [StringLiteral] "baz"
|
||||
# 27| getComponent: [StringTextComponent] baz
|
||||
# 27| getBlock: [BraceBlock] { ... }
|
||||
# 27| getParameter: [SimpleParameter] __synth__0__1
|
||||
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
|
||||
# 27| getStmt: [AssignExpr] ... = ...
|
||||
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
|
||||
# 28| getStmt: [AssignAddExpr] ... += ...
|
||||
# 28| getDesugared: [AssignExpr] ... = ...
|
||||
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
|
||||
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
|
||||
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
|
||||
# 29| getStmt: [LocalVariableAccess] xs
|
||||
gems/test.gemspec:
|
||||
# 2| [AssignExpr] ... = ...
|
||||
# 2| getDesugared: [StmtSequence] ...
|
||||
|
||||
@@ -10,6 +10,8 @@ exprValue
|
||||
| calls/calls.rb:26:7:26:7 | 1 | 1 | int |
|
||||
| calls/calls.rb:36:9:36:11 | 100 | 100 | int |
|
||||
| calls/calls.rb:36:14:36:16 | 200 | 200 | int |
|
||||
| calls/calls.rb:226:5:226:5 | nil | nil | nil |
|
||||
| calls/calls.rb:229:5:229:5 | nil | nil | nil |
|
||||
| calls/calls.rb:280:5:280:8 | :blah | :blah | symbol |
|
||||
| calls/calls.rb:281:5:281:8 | :blah | :blah | symbol |
|
||||
| calls/calls.rb:290:11:290:16 | "blah" | blah | string |
|
||||
@@ -58,8 +60,11 @@ exprValue
|
||||
| calls/calls.rb:322:37:322:37 | 2 | 2 | int |
|
||||
| calls/calls.rb:330:31:330:37 | "error" | error | string |
|
||||
| calls/calls.rb:342:5:342:5 | 0 | 0 | int |
|
||||
| calls/calls.rb:342:5:342:5 | nil | nil | nil |
|
||||
| calls/calls.rb:342:8:342:8 | 1 | 1 | int |
|
||||
| calls/calls.rb:342:8:342:8 | nil | nil | nil |
|
||||
| calls/calls.rb:342:11:342:11 | 2 | 2 | int |
|
||||
| calls/calls.rb:342:11:342:11 | nil | nil | nil |
|
||||
| calls/calls.rb:342:18:342:18 | 1 | 1 | int |
|
||||
| calls/calls.rb:342:20:342:20 | 2 | 2 | int |
|
||||
| calls/calls.rb:342:22:342:22 | 3 | 3 | int |
|
||||
@@ -330,18 +335,24 @@ exprValue
|
||||
| control/loops.rb:4:5:4:5 | 0 | 0 | int |
|
||||
| control/loops.rb:5:5:5:5 | 0 | 0 | int |
|
||||
| control/loops.rb:6:5:6:5 | 0 | 0 | int |
|
||||
| control/loops.rb:9:5:9:5 | nil | nil | nil |
|
||||
| control/loops.rb:9:10:9:10 | 1 | 1 | int |
|
||||
| control/loops.rb:9:13:9:14 | 10 | 10 | int |
|
||||
| control/loops.rb:16:5:16:5 | nil | nil | nil |
|
||||
| control/loops.rb:16:10:16:10 | 1 | 1 | int |
|
||||
| control/loops.rb:16:13:16:14 | 10 | 10 | int |
|
||||
| control/loops.rb:22:5:22:7 | 0 | 0 | int |
|
||||
| control/loops.rb:22:5:22:7 | nil | nil | nil |
|
||||
| control/loops.rb:22:10:22:14 | 1 | 1 | int |
|
||||
| control/loops.rb:22:10:22:14 | nil | nil | nil |
|
||||
| control/loops.rb:22:20:22:22 | :foo | :foo | symbol |
|
||||
| control/loops.rb:22:25:22:25 | 0 | 0 | int |
|
||||
| control/loops.rb:22:28:22:30 | :bar | :bar | symbol |
|
||||
| control/loops.rb:22:33:22:33 | 1 | 1 | int |
|
||||
| control/loops.rb:28:6:28:8 | 0 | 0 | int |
|
||||
| control/loops.rb:28:6:28:8 | nil | nil | nil |
|
||||
| control/loops.rb:28:11:28:15 | 1 | 1 | int |
|
||||
| control/loops.rb:28:11:28:15 | nil | nil | nil |
|
||||
| control/loops.rb:28:22:28:24 | :foo | :foo | symbol |
|
||||
| control/loops.rb:28:27:28:27 | 0 | 0 | int |
|
||||
| control/loops.rb:28:30:28:32 | :bar | :bar | symbol |
|
||||
@@ -365,6 +376,7 @@ exprValue
|
||||
| control/loops.rb:66:11:66:11 | y | 0 | int |
|
||||
| erb/template.html.erb:19:5:19:17 | "hello world" | hello world | string |
|
||||
| erb/template.html.erb:25:9:25:10 | "" | | string |
|
||||
| erb/template.html.erb:27:10:27:10 | nil | nil | nil |
|
||||
| erb/template.html.erb:27:16:27:20 | "foo" | foo | string |
|
||||
| erb/template.html.erb:27:23:27:27 | "bar" | bar | string |
|
||||
| erb/template.html.erb:27:30:27:34 | "baz" | baz | string |
|
||||
@@ -934,6 +946,8 @@ exprCfgNodeValue
|
||||
| calls/calls.rb:26:7:26:7 | 1 | 1 | int |
|
||||
| calls/calls.rb:36:9:36:11 | 100 | 100 | int |
|
||||
| calls/calls.rb:36:14:36:16 | 200 | 200 | int |
|
||||
| calls/calls.rb:226:5:226:5 | nil | nil | nil |
|
||||
| calls/calls.rb:229:5:229:5 | nil | nil | nil |
|
||||
| calls/calls.rb:280:5:280:8 | :blah | :blah | symbol |
|
||||
| calls/calls.rb:281:5:281:8 | :blah | :blah | symbol |
|
||||
| calls/calls.rb:290:11:290:16 | "blah" | blah | string |
|
||||
@@ -982,8 +996,11 @@ exprCfgNodeValue
|
||||
| calls/calls.rb:322:37:322:37 | 2 | 2 | int |
|
||||
| calls/calls.rb:330:31:330:37 | "error" | error | string |
|
||||
| calls/calls.rb:342:5:342:5 | 0 | 0 | int |
|
||||
| calls/calls.rb:342:5:342:5 | nil | nil | nil |
|
||||
| calls/calls.rb:342:8:342:8 | 1 | 1 | int |
|
||||
| calls/calls.rb:342:8:342:8 | nil | nil | nil |
|
||||
| calls/calls.rb:342:11:342:11 | 2 | 2 | int |
|
||||
| calls/calls.rb:342:11:342:11 | nil | nil | nil |
|
||||
| calls/calls.rb:342:18:342:18 | 1 | 1 | int |
|
||||
| calls/calls.rb:342:20:342:20 | 2 | 2 | int |
|
||||
| calls/calls.rb:342:22:342:22 | 3 | 3 | int |
|
||||
@@ -1226,18 +1243,24 @@ exprCfgNodeValue
|
||||
| control/loops.rb:4:5:4:5 | 0 | 0 | int |
|
||||
| control/loops.rb:5:5:5:5 | 0 | 0 | int |
|
||||
| control/loops.rb:6:5:6:5 | 0 | 0 | int |
|
||||
| control/loops.rb:9:5:9:5 | nil | nil | nil |
|
||||
| control/loops.rb:9:10:9:10 | 1 | 1 | int |
|
||||
| control/loops.rb:9:13:9:14 | 10 | 10 | int |
|
||||
| control/loops.rb:16:5:16:5 | nil | nil | nil |
|
||||
| control/loops.rb:16:10:16:10 | 1 | 1 | int |
|
||||
| control/loops.rb:16:13:16:14 | 10 | 10 | int |
|
||||
| control/loops.rb:22:5:22:7 | 0 | 0 | int |
|
||||
| control/loops.rb:22:5:22:7 | nil | nil | nil |
|
||||
| control/loops.rb:22:10:22:14 | 1 | 1 | int |
|
||||
| control/loops.rb:22:10:22:14 | nil | nil | nil |
|
||||
| control/loops.rb:22:20:22:22 | :foo | :foo | symbol |
|
||||
| control/loops.rb:22:25:22:25 | 0 | 0 | int |
|
||||
| control/loops.rb:22:28:22:30 | :bar | :bar | symbol |
|
||||
| control/loops.rb:22:33:22:33 | 1 | 1 | int |
|
||||
| control/loops.rb:28:6:28:8 | 0 | 0 | int |
|
||||
| control/loops.rb:28:6:28:8 | nil | nil | nil |
|
||||
| control/loops.rb:28:11:28:15 | 1 | 1 | int |
|
||||
| control/loops.rb:28:11:28:15 | nil | nil | nil |
|
||||
| control/loops.rb:28:22:28:24 | :foo | :foo | symbol |
|
||||
| control/loops.rb:28:27:28:27 | 0 | 0 | int |
|
||||
| control/loops.rb:28:30:28:32 | :bar | :bar | symbol |
|
||||
@@ -1261,6 +1284,7 @@ exprCfgNodeValue
|
||||
| control/loops.rb:66:11:66:11 | y | 0 | int |
|
||||
| erb/template.html.erb:19:5:19:17 | "hello world" | hello world | string |
|
||||
| erb/template.html.erb:25:9:25:10 | "" | | string |
|
||||
| erb/template.html.erb:27:10:27:10 | nil | nil | nil |
|
||||
| erb/template.html.erb:27:16:27:20 | "foo" | foo | string |
|
||||
| erb/template.html.erb:27:23:27:27 | "bar" | bar | string |
|
||||
| erb/template.html.erb:27:30:27:34 | "baz" | baz | string |
|
||||
|
||||
@@ -251,9 +251,13 @@ callsWithReceiver
|
||||
| calls.rb:223:1:223:6 | call to bar | calls.rb:223:1:223:1 | X |
|
||||
| calls.rb:223:14:223:19 | call to foo | calls.rb:223:14:223:14 | X |
|
||||
| calls.rb:226:1:228:3 | call to each | calls.rb:226:10:226:12 | call to bar |
|
||||
| calls.rb:226:5:226:5 | ! ... | calls.rb:226:5:226:5 | defined? ... |
|
||||
| calls.rb:226:5:226:5 | defined? ... | calls.rb:226:5:226:5 | x |
|
||||
| calls.rb:226:10:226:12 | call to bar | calls.rb:226:10:226:12 | self |
|
||||
| calls.rb:227:3:227:5 | call to baz | calls.rb:227:3:227:5 | self |
|
||||
| calls.rb:229:1:231:3 | call to each | calls.rb:229:10:229:15 | call to bar |
|
||||
| calls.rb:229:5:229:5 | ! ... | calls.rb:229:5:229:5 | defined? ... |
|
||||
| calls.rb:229:5:229:5 | defined? ... | calls.rb:229:5:229:5 | x |
|
||||
| calls.rb:229:10:229:15 | call to bar | calls.rb:229:10:229:10 | X |
|
||||
| calls.rb:230:3:230:8 | call to baz | calls.rb:230:3:230:3 | X |
|
||||
| calls.rb:234:1:234:3 | call to foo | calls.rb:234:1:234:3 | self |
|
||||
@@ -376,9 +380,15 @@ callsWithReceiver
|
||||
| calls.rb:338:3:338:13 | call to bar | calls.rb:338:3:338:13 | self |
|
||||
| calls.rb:342:1:344:3 | * ... | calls.rb:342:1:344:3 | __synth__0__1 |
|
||||
| calls.rb:342:1:344:3 | call to each | calls.rb:342:16:342:33 | [...] |
|
||||
| calls.rb:342:5:342:5 | ! ... | calls.rb:342:5:342:5 | defined? ... |
|
||||
| calls.rb:342:5:342:5 | call to [] | calls.rb:342:5:342:5 | __synth__3__1 |
|
||||
| calls.rb:342:5:342:5 | defined? ... | calls.rb:342:5:342:5 | x |
|
||||
| calls.rb:342:8:342:8 | ! ... | calls.rb:342:8:342:8 | defined? ... |
|
||||
| calls.rb:342:8:342:8 | call to [] | calls.rb:342:8:342:8 | __synth__3__1 |
|
||||
| calls.rb:342:8:342:8 | defined? ... | calls.rb:342:8:342:8 | y |
|
||||
| calls.rb:342:11:342:11 | ! ... | calls.rb:342:11:342:11 | defined? ... |
|
||||
| calls.rb:342:11:342:11 | call to [] | calls.rb:342:11:342:11 | __synth__3__1 |
|
||||
| calls.rb:342:11:342:11 | defined? ... | calls.rb:342:11:342:11 | z |
|
||||
| calls.rb:342:16:342:33 | call to [] | calls.rb:342:16:342:33 | Array |
|
||||
| calls.rb:342:17:342:23 | call to [] | calls.rb:342:17:342:23 | Array |
|
||||
| calls.rb:342:26:342:32 | call to [] | calls.rb:342:26:342:32 | Array |
|
||||
|
||||
@@ -22,6 +22,12 @@ conditionalExprs
|
||||
| conditionals.rb:61:1:64:3 | if ... | IfExpr | conditionals.rb:61:4:61:8 | ... > ... | conditionals.rb:63:1:63:4 | else ... | false |
|
||||
| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:67:10:67:13 | then ... | true |
|
||||
| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:68:1:69:5 | else ... | false |
|
||||
| loops.rb:9:5:9:5 | if ... | IfExpr | loops.rb:9:5:9:5 | ! ... | loops.rb:9:5:9:5 | ... = ... | true |
|
||||
| loops.rb:16:5:16:5 | if ... | IfExpr | loops.rb:16:5:16:5 | ! ... | loops.rb:16:5:16:5 | ... = ... | true |
|
||||
| loops.rb:22:5:22:7 | if ... | IfExpr | loops.rb:22:5:22:7 | ! ... | loops.rb:22:5:22:7 | ... = ... | true |
|
||||
| loops.rb:22:10:22:14 | if ... | IfExpr | loops.rb:22:10:22:14 | ! ... | loops.rb:22:10:22:14 | ... = ... | true |
|
||||
| loops.rb:28:6:28:8 | if ... | IfExpr | loops.rb:28:6:28:8 | ! ... | loops.rb:28:6:28:8 | ... = ... | true |
|
||||
| loops.rb:28:11:28:15 | if ... | IfExpr | loops.rb:28:11:28:15 | ! ... | loops.rb:28:11:28:15 | ... = ... | true |
|
||||
ifExprs
|
||||
| conditionals.rb:10:1:12:3 | if ... | IfExpr | conditionals.rb:10:4:10:8 | ... > ... | conditionals.rb:10:10:11:5 | then ... | (none) | false |
|
||||
| conditionals.rb:15:1:19:3 | if ... | IfExpr | conditionals.rb:15:4:15:9 | ... == ... | conditionals.rb:15:10:16:5 | then ... | else ... | false |
|
||||
|
||||
@@ -27,9 +27,15 @@
|
||||
| conditionals.rb:61:1:64:3 | if ... | IfExpr |
|
||||
| conditionals.rb:67:1:70:3 | if ... | IfExpr |
|
||||
| loops.rb:9:1:12:3 | for ... in ... | ForExpr |
|
||||
| loops.rb:9:5:9:5 | if ... | IfExpr |
|
||||
| loops.rb:16:1:19:3 | for ... in ... | ForExpr |
|
||||
| loops.rb:16:5:16:5 | if ... | IfExpr |
|
||||
| loops.rb:22:1:25:3 | for ... in ... | ForExpr |
|
||||
| loops.rb:22:5:22:7 | if ... | IfExpr |
|
||||
| loops.rb:22:10:22:14 | if ... | IfExpr |
|
||||
| loops.rb:28:1:32:3 | for ... in ... | ForExpr |
|
||||
| loops.rb:28:6:28:8 | if ... | IfExpr |
|
||||
| loops.rb:28:11:28:15 | if ... | IfExpr |
|
||||
| loops.rb:35:1:39:3 | while ... | WhileExpr |
|
||||
| loops.rb:42:1:45:3 | while ... | WhileExpr |
|
||||
| loops.rb:48:1:48:19 | ... while ... | WhileModifierExpr |
|
||||
|
||||
@@ -2377,7 +2377,7 @@ cfg.rb:
|
||||
#-----| -> \u1234
|
||||
|
||||
# 88| ... = ...
|
||||
#-----| -> Array
|
||||
#-----| -> x
|
||||
|
||||
# 88| "\u1234#{...}\n"
|
||||
#-----| -> ... = ...
|
||||
@@ -2394,9 +2394,21 @@ cfg.rb:
|
||||
# 88| \n
|
||||
#-----| -> "\u1234#{...}\n"
|
||||
|
||||
# 90| ...
|
||||
#-----| -> $global
|
||||
|
||||
# 90| ... = ...
|
||||
#-----| -> if ...
|
||||
|
||||
# 90| ... = ...
|
||||
#-----| -> x
|
||||
|
||||
# 90| [false] ! ...
|
||||
#-----| false -> if ...
|
||||
|
||||
# 90| [true] ! ...
|
||||
#-----| true -> x
|
||||
|
||||
# 90| __synth__0__1
|
||||
#-----| -> x
|
||||
|
||||
@@ -2404,7 +2416,11 @@ cfg.rb:
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 90| call to each
|
||||
#-----| -> $global
|
||||
#-----| -> ...
|
||||
|
||||
# 90| defined? ...
|
||||
#-----| false -> [true] ! ...
|
||||
#-----| true -> [false] ! ...
|
||||
|
||||
# 90| enter { ... }
|
||||
#-----| -> __synth__0__1
|
||||
@@ -2414,6 +2430,18 @@ cfg.rb:
|
||||
# 90| exit { ... } (normal)
|
||||
#-----| -> exit { ... }
|
||||
|
||||
# 90| if ...
|
||||
#-----| -> Array
|
||||
|
||||
# 90| nil
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 90| x
|
||||
#-----| -> nil
|
||||
|
||||
# 90| x
|
||||
#-----| -> defined? ...
|
||||
|
||||
# 90| { ... }
|
||||
#-----| -> call to each
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ callsWithNoArguments
|
||||
| cfg.rb:62:7:62:12 | * ... |
|
||||
| cfg.rb:62:17:62:27 | * ... |
|
||||
| cfg.rb:90:1:93:3 | call to each |
|
||||
| cfg.rb:90:5:90:5 | [false] ! ... |
|
||||
| cfg.rb:90:5:90:5 | [true] ! ... |
|
||||
| cfg.rb:90:5:90:5 | defined? ... |
|
||||
| cfg.rb:98:10:98:15 | ** ... |
|
||||
| cfg.rb:98:30:98:35 | ** ... |
|
||||
| cfg.rb:138:17:138:23 | * ... |
|
||||
|
||||
@@ -15,7 +15,6 @@ testFailures
|
||||
| array_flow.rb:376:10:376:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
|
||||
| array_flow.rb:377:10:377:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
|
||||
| array_flow.rb:378:10:378:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
|
||||
| array_flow.rb:407:12:407:30 | # $ hasValueFlow=45 | Missing result:hasValueFlow=45 |
|
||||
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
|
||||
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.4 |
|
||||
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
|
||||
|
||||
@@ -2399,6 +2399,7 @@
|
||||
| UseUseExplosion.rb:24:5:25:7 | use | UseUseExplosion.rb:1:1:26:3 | C |
|
||||
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
|
||||
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
|
||||
| local_dataflow.rb:1:1:150:3 | <uninitialized> | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
|
||||
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
|
||||
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
|
||||
@@ -2424,21 +2425,33 @@
|
||||
| local_dataflow.rb:9:1:9:5 | array | local_dataflow.rb:10:14:10:18 | array |
|
||||
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:5 | array |
|
||||
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | ... | local_dataflow.rb:10:1:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | <captured entry> self | local_dataflow.rb:11:1:11:2 | self |
|
||||
| local_dataflow.rb:10:5:13:3 | <captured exit> x | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:5:13:3 | ... |
|
||||
| local_dataflow.rb:10:9:10:9 | ... = ... | local_dataflow.rb:10:9:10:9 | if ... |
|
||||
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | ... = ... |
|
||||
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | phi |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
|
||||
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
|
||||
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
|
||||
| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self |
|
||||
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self |
|
||||
| local_dataflow.rb:15:1:17:3 | <captured exit> x | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:15:1:17:3 | call to each | local_dataflow.rb:15:1:17:3 | ... |
|
||||
| local_dataflow.rb:15:5:15:5 | ... = ... | local_dataflow.rb:15:5:15:5 | if ... |
|
||||
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | ... = ... |
|
||||
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | phi |
|
||||
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
|
||||
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
|
||||
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
|
||||
@@ -2446,6 +2459,11 @@
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:19:1:21:3 | call to each | local_dataflow.rb:19:1:21:3 | ... |
|
||||
| local_dataflow.rb:19:5:19:5 | ... = ... | local_dataflow.rb:19:5:19:5 | if ... |
|
||||
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | ... = ... |
|
||||
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | phi |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
|
||||
| local_dataflow.rb:24:2:24:8 | break | local_dataflow.rb:23:1:25:3 | while ... |
|
||||
| local_dataflow.rb:24:8:24:8 | 5 | local_dataflow.rb:24:2:24:8 | break |
|
||||
|
||||
@@ -832,13 +832,22 @@ arg
|
||||
| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | position 1 |
|
||||
| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | position 2 |
|
||||
| local_dataflow.rb:10:5:13:3 | { ... } | local_dataflow.rb:10:5:13:3 | call to each | block |
|
||||
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [false] ! ... | self |
|
||||
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [true] ! ... | self |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | defined? ... | self |
|
||||
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | call to each | self |
|
||||
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | self |
|
||||
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | self |
|
||||
| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | position 0 |
|
||||
| local_dataflow.rb:15:1:17:3 | { ... } | local_dataflow.rb:15:1:17:3 | call to each | block |
|
||||
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [false] ! ... | self |
|
||||
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [true] ! ... | self |
|
||||
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | defined? ... | self |
|
||||
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | call to each | self |
|
||||
| local_dataflow.rb:19:1:21:3 | { ... } | local_dataflow.rb:19:1:21:3 | call to each | block |
|
||||
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [false] ! ... | self |
|
||||
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [true] ! ... | self |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | defined? ... | self |
|
||||
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | call to each | self |
|
||||
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | self |
|
||||
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | position 0 |
|
||||
|
||||
@@ -2842,6 +2842,7 @@
|
||||
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
|
||||
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
|
||||
| local_dataflow.rb:1:1:7:3 | synthetic *args | local_dataflow.rb:1:9:1:9 | a |
|
||||
| local_dataflow.rb:1:1:150:3 | <uninitialized> | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
|
||||
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
|
||||
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
|
||||
@@ -2870,23 +2871,41 @@
|
||||
| local_dataflow.rb:9:9:9:15 | Array | local_dataflow.rb:9:9:9:15 | call to [] |
|
||||
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:5 | array |
|
||||
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | ... | local_dataflow.rb:10:1:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | <captured entry> self | local_dataflow.rb:11:1:11:2 | self |
|
||||
| local_dataflow.rb:10:5:13:3 | <captured exit> x | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
|
||||
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:5:13:3 | ... |
|
||||
| local_dataflow.rb:10:5:13:3 | synthetic *args | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:10:9:10:9 | ... = ... | local_dataflow.rb:10:9:10:9 | if ... |
|
||||
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [false] ! ... |
|
||||
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [true] ! ... |
|
||||
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | ... = ... |
|
||||
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | x |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | defined? ... |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | phi |
|
||||
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
|
||||
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
|
||||
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
|
||||
| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self |
|
||||
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self |
|
||||
| local_dataflow.rb:15:1:17:3 | <captured exit> x | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:15:1:17:3 | call to each | local_dataflow.rb:15:1:17:3 | ... |
|
||||
| local_dataflow.rb:15:1:17:3 | synthetic *args | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:15:5:15:5 | ... = ... | local_dataflow.rb:15:5:15:5 | if ... |
|
||||
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [false] ! ... |
|
||||
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [true] ! ... |
|
||||
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | ... = ... |
|
||||
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | x |
|
||||
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | defined? ... |
|
||||
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | phi |
|
||||
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
|
||||
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
|
||||
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
|
||||
@@ -2894,7 +2913,15 @@
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:19:1:21:3 | call to each | local_dataflow.rb:19:1:21:3 | ... |
|
||||
| local_dataflow.rb:19:1:21:3 | synthetic *args | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
|
||||
| local_dataflow.rb:19:5:19:5 | ... = ... | local_dataflow.rb:19:5:19:5 | if ... |
|
||||
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [false] ! ... |
|
||||
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [true] ! ... |
|
||||
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | ... = ... |
|
||||
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | x |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | defined? ... |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | phi |
|
||||
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
|
||||
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... |
|
||||
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... |
|
||||
|
||||
@@ -124,6 +124,8 @@ definition
|
||||
| ssa.rb:26:3:28:5 | <captured exit> elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:32:1:36:3 | self (m3) | ssa.rb:32:1:36:3 | self |
|
||||
| ssa.rb:33:16:35:5 | <captured entry> self | ssa.rb:32:1:36:3 | self |
|
||||
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
|
||||
@@ -312,6 +314,7 @@ read
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
|
||||
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
|
||||
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
|
||||
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
|
||||
@@ -466,6 +469,7 @@ firstRead
|
||||
| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i |
|
||||
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
|
||||
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
|
||||
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
|
||||
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
|
||||
@@ -617,6 +621,7 @@ lastRead
|
||||
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
|
||||
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
|
||||
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
|
||||
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
|
||||
@@ -732,6 +737,8 @@ phi
|
||||
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:5 | i |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
|
||||
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
|
||||
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | <uninitialized> |
|
||||
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | <uninitialized> |
|
||||
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x |
|
||||
| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | <uninitialized> |
|
||||
|
||||
@@ -226,6 +226,8 @@ variableAccess
|
||||
| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 |
|
||||
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:27:5:27:13 | self | ssa.rb:25:1:30:3 | self | ssa.rb:25:1:30:3 | m2 |
|
||||
| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
|
||||
@@ -354,6 +356,7 @@ explicitWrite
|
||||
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... -= ... |
|
||||
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... = ... |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:3:28:5 | ... = ... |
|
||||
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | ... = ... |
|
||||
| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:9 | ... = ... |
|
||||
| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
|
||||
| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... |
|
||||
@@ -567,6 +570,7 @@ readAccess
|
||||
| ssa.rb:20:10:20:10 | x |
|
||||
| ssa.rb:21:5:21:5 | x |
|
||||
| ssa.rb:26:3:28:5 | __synth__0__1 |
|
||||
| ssa.rb:26:7:26:10 | elem |
|
||||
| ssa.rb:26:15:26:22 | elements |
|
||||
| ssa.rb:27:5:27:13 | self |
|
||||
| ssa.rb:27:10:27:13 | elem |
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
| tst.rb:137:11:137:17 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
|
||||
| tst.rb:143:11:143:18 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| tst.rb:146:11:146:17 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
|
||||
| tst.rb:149:11:149:20 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| tst.rb:152:11:152:24 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
|
||||
| tst.rb:155:11:155:20 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
|
||||
| tst.rb:149:11:149:20 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| tst.rb:152:11:152:24 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
|
||||
| tst.rb:155:11:155:20 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
|
||||
| tst.rb:158:11:158:18 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. |
|
||||
| tst.rb:161:11:161:18 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
| tst.rb:164:11:164:20 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
private import codeql.regex.RegexTreeView
|
||||
private import codeql.util.Numbers
|
||||
|
||||
/**
|
||||
* Classes and predicates that create an NFA and various algorithms for working with it.
|
||||
@@ -17,6 +18,20 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
exists(int code | code = ascii(c) | code + 1 = ascii(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th codepoint in `s`.
|
||||
*/
|
||||
bindingset[s]
|
||||
private string getCodepointAt(string s, int i) { result = s.regexpFind("(.|\\s)", i, _) }
|
||||
|
||||
/**
|
||||
* Gets the length of `s` in codepoints.
|
||||
*/
|
||||
bindingset[str]
|
||||
private int getCodepointLength(string str) {
|
||||
result = str.regexpReplaceAll("(.|\\s)", "x").length()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an approximation for the ASCII code for `char`.
|
||||
* Only the easily printable chars are included (so no newline, tab, null, etc).
|
||||
@@ -189,17 +204,17 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
/** An input symbol corresponding to character `c`. */
|
||||
Char(string c) {
|
||||
c =
|
||||
any(RegexpCharacterConstant cc |
|
||||
cc instanceof RelevantRegExpTerm and
|
||||
not isIgnoreCase(cc.getRootTerm())
|
||||
).getValue().charAt(_)
|
||||
getCodepointAt(any(RegexpCharacterConstant cc |
|
||||
cc instanceof RelevantRegExpTerm and
|
||||
not isIgnoreCase(cc.getRootTerm())
|
||||
).getValue(), _)
|
||||
or
|
||||
// normalize everything to lower case if the regexp is case insensitive
|
||||
c =
|
||||
any(RegexpCharacterConstant cc, string char |
|
||||
cc instanceof RelevantRegExpTerm and
|
||||
isIgnoreCase(cc.getRootTerm()) and
|
||||
char = cc.getValue().charAt(_)
|
||||
char = getCodepointAt(cc.getValue(), _)
|
||||
|
|
||||
char.toLowerCase()
|
||||
)
|
||||
@@ -395,7 +410,7 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
string getARelevantChar() {
|
||||
exists(ascii(result))
|
||||
or
|
||||
exists(RegexpCharacterConstant c | result = c.getValue().charAt(_))
|
||||
exists(RegexpCharacterConstant c | result = getCodepointAt(c.getValue(), _))
|
||||
or
|
||||
classEscapeMatches(_, result)
|
||||
}
|
||||
@@ -693,6 +708,12 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private int getCodepointLengthForState(string s) {
|
||||
result = getCodepointLength(s) and
|
||||
s = any(RegexpCharacterConstant reg).getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`.
|
||||
*/
|
||||
@@ -701,16 +722,16 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
q1 = Match(s, i) and
|
||||
(
|
||||
not isIgnoreCase(s.getRootTerm()) and
|
||||
lbl = Char(s.getValue().charAt(i))
|
||||
lbl = Char(getCodepointAt(s.getValue(), i))
|
||||
or
|
||||
// normalize everything to lower case if the regexp is case insensitive
|
||||
isIgnoreCase(s.getRootTerm()) and
|
||||
exists(string c | c = s.getValue().charAt(i) | lbl = Char(c.toLowerCase()))
|
||||
exists(string c | c = getCodepointAt(s.getValue(), i) | lbl = Char(c.toLowerCase()))
|
||||
) and
|
||||
(
|
||||
q2 = Match(s, i + 1)
|
||||
or
|
||||
s.getValue().length() = i + 1 and
|
||||
getCodepointLengthForState(s.getValue()) = i + 1 and
|
||||
q2 = after(s)
|
||||
)
|
||||
)
|
||||
@@ -811,7 +832,7 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
Match(RelevantRegExpTerm t, int i) {
|
||||
i = 0
|
||||
or
|
||||
exists(t.(RegexpCharacterConstant).getValue().charAt(i))
|
||||
exists(getCodepointAt(t.(RegexpCharacterConstant).getValue(), i))
|
||||
} or
|
||||
/**
|
||||
* An accept state, where exactly the given input string is accepted.
|
||||
@@ -1104,7 +1125,9 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
*/
|
||||
predicate reachesOnlyRejectableSuffixes(State fork, string w) {
|
||||
isReDoSCandidate(fork, w) and
|
||||
forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) and
|
||||
forex(State next | next = process(fork, w, getCodepointLengthForCandidate(w) - 1) |
|
||||
isLikelyRejectable(next)
|
||||
) and
|
||||
not getProcessPrevious(fork, _, w) = acceptsAnySuffix() // we stop `process(..)` early if we can, check here if it happened.
|
||||
}
|
||||
|
||||
@@ -1214,6 +1237,13 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _))
|
||||
}
|
||||
|
||||
// `process` can't use pragma[inline] predicates. So a materialized version of `getCodepointAt` is needed.
|
||||
pragma[noinline]
|
||||
private string getCodePointAtForProcess(string str, int i) {
|
||||
result = getCodepointAt(str, i) and
|
||||
isReDoSCandidate(_, str)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a state that can be reached from pumpable `fork` consuming all
|
||||
* chars in `w` any number of times followed by the first `i+1` characters of `w`.
|
||||
@@ -1223,7 +1253,7 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
exists(State prev | prev = getProcessPrevious(fork, i, w) |
|
||||
not prev = acceptsAnySuffix() and // we stop `process(..)` early if we can. If the successor accepts any suffix, then we know it can never be rejected.
|
||||
exists(string char, InputSymbol sym |
|
||||
char = w.charAt(i) and
|
||||
char = getCodePointAtForProcess(w, i) and
|
||||
deltaClosed(prev, sym, result) and
|
||||
// noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`.
|
||||
// Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found.
|
||||
@@ -1232,6 +1262,12 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private int getCodepointLengthForCandidate(string s) {
|
||||
result = getCodepointLength(s) and
|
||||
isReDoSCandidate(_, s)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a state that can be reached from pumpable `fork` consuming all
|
||||
* chars in `w` any number of times followed by the first `i` characters of `w`.
|
||||
@@ -1245,7 +1281,7 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
or
|
||||
// repeat until fixpoint
|
||||
i = 0 and
|
||||
result = process(fork, w, w.length() - 1)
|
||||
result = process(fork, w, getCodepointLengthForCandidate(w) - 1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1261,7 +1297,9 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
/**
|
||||
* Gets a `char` that occurs in a `pump` string.
|
||||
*/
|
||||
private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) }
|
||||
private string getAProcessChar() {
|
||||
result = getCodepointAt(any(string s | isReDoSCandidate(_, s)), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1305,10 +1343,40 @@ module Make<RegexTreeViewSig TreeImpl> {
|
||||
bindingset[s]
|
||||
private string escape(string s) {
|
||||
result =
|
||||
s.replaceAll("\\", "\\\\")
|
||||
.replaceAll("\n", "\\n")
|
||||
.replaceAll("\r", "\\r")
|
||||
.replaceAll("\t", "\\t")
|
||||
escapeUnicodeString(s.replaceAll("\\", "\\\\")
|
||||
.replaceAll("\n", "\\n")
|
||||
.replaceAll("\r", "\\r")
|
||||
.replaceAll("\t", "\\t"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string where the unicode characters in `s` have been escaped.
|
||||
*/
|
||||
bindingset[s]
|
||||
private string escapeUnicodeString(string s) {
|
||||
result =
|
||||
concat(int i, string char | char = escapeUnicodeChar(getCodepointAt(s, i)) | char order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unicode escaped string for `char`.
|
||||
* If `char` is a printable char, then `char` is returned.
|
||||
*/
|
||||
bindingset[char]
|
||||
private string escapeUnicodeChar(string char) {
|
||||
if isPrintable(char)
|
||||
then result = char
|
||||
else
|
||||
if exists(to4digitHex(any(int i | i.toUnicode() = char)))
|
||||
then result = "\\u" + to4digitHex(any(int i | i.toUnicode() = char))
|
||||
else result = "\\u{" + toHex(any(int i | i.toUnicode() = char)) + "}"
|
||||
}
|
||||
|
||||
/** Holds if `char` is easily printable char, or whitespace. */
|
||||
private predicate isPrintable(string char) {
|
||||
exists(ascii(char))
|
||||
or
|
||||
char = "\n\r\t".charAt(_)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,4 +3,5 @@ version: 0.1.3-dev
|
||||
groups: shared
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/util: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -50,7 +50,7 @@ int parseHexInt(string hex) {
|
||||
sum(int index, string c |
|
||||
c = stripped.charAt(index)
|
||||
|
|
||||
sixteenToThe(stripped.length() - 1 - index) * toHex(c)
|
||||
sixteenToThe(stripped.length() - 1 - index) * charToHex(c)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ int parseOctalInt(string octal) {
|
||||
}
|
||||
|
||||
/** Gets the integer value of the `hex` char. */
|
||||
private int toHex(string hex) {
|
||||
private int charToHex(string hex) {
|
||||
hex = [0 .. 9].toString() and
|
||||
result = hex.toInt()
|
||||
or
|
||||
@@ -100,6 +100,32 @@ private int toHex(string hex) {
|
||||
result = 15 and hex = ["f", "F"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a 4-digit hex representation of `i`.
|
||||
*/
|
||||
bindingset[i]
|
||||
string to4digitHex(int i) {
|
||||
i >= 0 and
|
||||
i <= 65535 and
|
||||
exists(string hex | hex = toHex(i) |
|
||||
result = concat(int zeroes | zeroes = [1 .. 4 - hex.length()] | "0") + hex
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hex representation of `i`.
|
||||
*/
|
||||
bindingset[i]
|
||||
string toHex(int i) {
|
||||
result =
|
||||
// make the number with lots of preceding zeroes, then remove all preceding zeroes in a post-processing step
|
||||
concat(int shift |
|
||||
shift in [28, 24, 20, 16, 12, 8, 4, 0]
|
||||
|
|
||||
"0123456789abcdef".charAt(i.bitShiftRight(shift).bitAnd(15)) order by shift desc
|
||||
).regexpReplaceAll("^0*", "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of 16 to the power of `n`. Holds only for `n` in the range
|
||||
* 0..7 (inclusive).
|
||||
|
||||
@@ -73,7 +73,7 @@ codeql::ForEachStmt StmtTranslator::translateForEachStmt(const swift::ForEachStm
|
||||
auto entry = dispatcher.createEntry(stmt);
|
||||
fillLabeledStmt(stmt, entry);
|
||||
entry.body = dispatcher.fetchLabel(stmt.getBody());
|
||||
entry.sequence = dispatcher.fetchLabel(stmt.getParsedSequence());
|
||||
entry.sequence = dispatcher.fetchLabel(stmt.getTypeCheckedSequence());
|
||||
entry.pattern = dispatcher.fetchLabel(stmt.getPattern());
|
||||
entry.where = dispatcher.fetchOptionalLabel(stmt.getWhere());
|
||||
return entry;
|
||||
|
||||
1
swift/ql/.generated.list
generated
1
swift/ql/.generated.list
generated
@@ -347,7 +347,6 @@ lib/codeql/swift/elements/type/StructTypeConstructor.qll a784445a9bb98bb59b866af
|
||||
lib/codeql/swift/elements/type/SubstitutableType.qll 78a240c6226c2167a85dce325f0f3c552364daf879c0309ebefd4787d792df23 cdc27e531f61fb50aaa9a20f5bf05c081759ac27df35e16afcdd2d1ecdac5da0
|
||||
lib/codeql/swift/elements/type/SugarType.qll 0833a0f1bd26b066817f55df7a58243dbd5da69051272c38effb45653170d5c1 cbcbd68098b76d99c09e7ee43c9e7d04e1b2e860df943a520bf793e835c4db81
|
||||
lib/codeql/swift/elements/type/SyntaxSugarType.qll 699fe9b4805494b62416dc86098342a725020f59a649138e6f5ba405dd536db5 a7a002cf597c3e3d0fda67111116c61a80f1e66ab8db8ddb3e189c6f15cadda6
|
||||
lib/codeql/swift/elements/type/TupleType.qll 1dc14882028be534d15e348fba318c0bb1b52e692ca833987e00c9a66a1921ad 0b34c17ce9db336d0be9a869da988f31f10f754d6ffab6fa88791e508044edd2
|
||||
lib/codeql/swift/elements/type/TupleTypeConstructor.qll 060633b22ee9884cb98103b380963fac62a02799461d342372cfb9cc6303d693 c9a89f695c85e7e22947287bcc32909b1f701168fd89c3598a45c97909e879f4
|
||||
lib/codeql/swift/elements/type/TypeAliasTypeConstructor.qll f63ada921beb95d5f3484ab072aa4412e93adfc8e7c0b1637273f99356f5cb13 f90d2789f7c922bc8254a0d131e36b40db1e00f9b32518633520d5c3341cd70a
|
||||
lib/codeql/swift/elements/type/TypeReprConstructor.qll 2bb9c5ece40c6caed9c3a614affc0efd47ad2309c09392800ad346bf369969bf 30429adc135eb8fc476bc9bc185cff0a4119ddc0e618368c44f4a43246b5287f
|
||||
|
||||
1
swift/ql/.gitattributes
generated
vendored
1
swift/ql/.gitattributes
generated
vendored
@@ -349,7 +349,6 @@
|
||||
/lib/codeql/swift/elements/type/SubstitutableType.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/SugarType.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/SyntaxSugarType.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/TupleType.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/TupleTypeConstructor.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/TypeAliasTypeConstructor.qll linguist-generated
|
||||
/lib/codeql/swift/elements/type/TypeReprConstructor.qll linguist-generated
|
||||
|
||||
5
swift/ql/lib/change-notes/2023-07-27-type-getname.md
Normal file
5
swift/ql/lib/change-notes/2023-07-27-type-getname.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
|
||||
* `Type.getName` now gets the name of the type alone without any enclosing types. Use `Type.getFullName` for the old behaviour.
|
||||
@@ -6,18 +6,4 @@ class NominalType extends Generated::NominalType {
|
||||
override Type getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
|
||||
|
||||
NominalType getADerivedType() { result.getABaseType() = this }
|
||||
|
||||
/**
|
||||
* Gets the full name of this `NominalType`. For example in:
|
||||
* ```swift
|
||||
* struct A {
|
||||
* struct B {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
|
||||
* full name of `B` is `A.B`.
|
||||
*/
|
||||
string getFullName() { result = this.getDeclaration().(NominalTypeDecl).getFullName() }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
|
||||
private import codeql.swift.generated.type.TupleType
|
||||
|
||||
/**
|
||||
* A tuple type, for example:
|
||||
* ```
|
||||
* (Int, String)
|
||||
* ```
|
||||
*/
|
||||
class TupleType extends Generated::TupleType { }
|
||||
|
||||
@@ -6,7 +6,31 @@ private import codeql.swift.generated.type.Type
|
||||
* This QL class is the root of the Swift type hierarchy.
|
||||
*/
|
||||
class Type extends Generated::Type {
|
||||
override string toString() { result = this.getName() }
|
||||
override string toString() { result = this.getFullName() }
|
||||
|
||||
/**
|
||||
* Gets the name of this type.
|
||||
*/
|
||||
override string getName() {
|
||||
// replace anything that looks like a full name `a.b.c` with just the
|
||||
// short name `c`, by removing the `a.` and `b.` parts. Note that this
|
||||
// has to be robust for tuple type names such as `(a, b.c)`.
|
||||
result = super.getName().regexpReplaceAll("[^(),. ]++\\.(?!\\.)", "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full name of this `Type`. For example in:
|
||||
* ```swift
|
||||
* struct A {
|
||||
* struct B {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
|
||||
* full name of `B` is `A.B`.
|
||||
*/
|
||||
string getFullName() { result = super.getName() }
|
||||
|
||||
/**
|
||||
* Gets this type after any type aliases have been resolved. For example in
|
||||
|
||||
@@ -158,7 +158,7 @@ private class ExtraStringLengthConflationSource extends StringLengthConflationSo
|
||||
or
|
||||
stringType = TStringUnicodeScalars()
|
||||
) and
|
||||
memberRef.getBase().getType().(NominalType).getName() = stringType.getName() and
|
||||
memberRef.getBase().getType().(NominalType).getFullName() = stringType.getName() and
|
||||
memberRef.getMember().(VarDecl).getName() = "count" and
|
||||
this.asExpr() = memberRef
|
||||
)
|
||||
@@ -218,8 +218,8 @@ private class ExtraStringLengthConflationSink extends StringLengthConflationSink
|
||||
stringType = TStringUnicodeScalars()
|
||||
) and
|
||||
(
|
||||
call.getQualifier().getType().(NominalType).getName() = stringType.getName() or
|
||||
call.getQualifier().getType().(InOutType).getObjectType().(NominalType).getName() =
|
||||
call.getQualifier().getType().(NominalType).getFullName() = stringType.getName() or
|
||||
call.getQualifier().getType().(InOutType).getObjectType().(NominalType).getFullName() =
|
||||
stringType.getName()
|
||||
) and
|
||||
(
|
||||
|
||||
@@ -127,7 +127,7 @@ private class DefaultUnsafeJsEvalAdditionalFlowStep extends UnsafeJsEvalAddition
|
||||
)
|
||||
or
|
||||
exists(MemberRefExpr e, Expr self, VarDecl member |
|
||||
self.getType().getName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and
|
||||
self.getType().getFullName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and
|
||||
member.getName() = "baseAddress"
|
||||
|
|
||||
e.getBase() = self and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Builtin.Int8 | getName: | Builtin.Int8 | getCanonicalType: | Builtin.Int8 | hasWidth: | yes |
|
||||
| Builtin.Int16 | getName: | Builtin.Int16 | getCanonicalType: | Builtin.Int16 | hasWidth: | yes |
|
||||
| Builtin.Int32 | getName: | Builtin.Int32 | getCanonicalType: | Builtin.Int32 | hasWidth: | yes |
|
||||
| Builtin.Int64 | getName: | Builtin.Int64 | getCanonicalType: | Builtin.Int64 | hasWidth: | yes |
|
||||
| Builtin.Word | getName: | Builtin.Word | getCanonicalType: | Builtin.Word | hasWidth: | no |
|
||||
| Builtin.Int8 | getName: | Int8 | getCanonicalType: | Builtin.Int8 | hasWidth: | yes |
|
||||
| Builtin.Int16 | getName: | Int16 | getCanonicalType: | Builtin.Int16 | hasWidth: | yes |
|
||||
| Builtin.Int32 | getName: | Int32 | getCanonicalType: | Builtin.Int32 | hasWidth: | yes |
|
||||
| Builtin.Int64 | getName: | Int64 | getCanonicalType: | Builtin.Int64 | hasWidth: | yes |
|
||||
| Builtin.Word | getName: | Word | getCanonicalType: | Builtin.Word | hasWidth: | no |
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| Builtin.BridgeObject | BuiltinBridgeObjectType | getName: | Builtin.BridgeObject | getCanonicalType: | Builtin.BridgeObject |
|
||||
| Builtin.Executor | BuiltinExecutorType | getName: | Builtin.Executor | getCanonicalType: | Builtin.Executor |
|
||||
| Builtin.FPIEEE32 | BuiltinFloatType | getName: | Builtin.FPIEEE32 | getCanonicalType: | Builtin.FPIEEE32 |
|
||||
| Builtin.FPIEEE64 | BuiltinFloatType | getName: | Builtin.FPIEEE64 | getCanonicalType: | Builtin.FPIEEE64 |
|
||||
| Builtin.IntLiteral | BuiltinIntegerLiteralType | getName: | Builtin.IntLiteral | getCanonicalType: | Builtin.IntLiteral |
|
||||
| Builtin.Job | BuiltinJobType | getName: | Builtin.Job | getCanonicalType: | Builtin.Job |
|
||||
| Builtin.NativeObject | BuiltinNativeObjectType | getName: | Builtin.NativeObject | getCanonicalType: | Builtin.NativeObject |
|
||||
| Builtin.RawPointer | BuiltinRawPointerType | getName: | Builtin.RawPointer | getCanonicalType: | Builtin.RawPointer |
|
||||
| Builtin.RawUnsafeContinuation | BuiltinRawUnsafeContinuationType | getName: | Builtin.RawUnsafeContinuation | getCanonicalType: | Builtin.RawUnsafeContinuation |
|
||||
| Builtin.BridgeObject | BuiltinBridgeObjectType | getName: | BridgeObject | getCanonicalType: | Builtin.BridgeObject |
|
||||
| Builtin.Executor | BuiltinExecutorType | getName: | Executor | getCanonicalType: | Builtin.Executor |
|
||||
| Builtin.FPIEEE32 | BuiltinFloatType | getName: | FPIEEE32 | getCanonicalType: | Builtin.FPIEEE32 |
|
||||
| Builtin.FPIEEE64 | BuiltinFloatType | getName: | FPIEEE64 | getCanonicalType: | Builtin.FPIEEE64 |
|
||||
| Builtin.IntLiteral | BuiltinIntegerLiteralType | getName: | IntLiteral | getCanonicalType: | Builtin.IntLiteral |
|
||||
| Builtin.Job | BuiltinJobType | getName: | Job | getCanonicalType: | Builtin.Job |
|
||||
| Builtin.NativeObject | BuiltinNativeObjectType | getName: | NativeObject | getCanonicalType: | Builtin.NativeObject |
|
||||
| Builtin.RawPointer | BuiltinRawPointerType | getName: | RawPointer | getCanonicalType: | Builtin.RawPointer |
|
||||
| Builtin.RawUnsafeContinuation | BuiltinRawUnsafeContinuationType | getName: | RawUnsafeContinuation | getCanonicalType: | Builtin.RawUnsafeContinuation |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (Builtin.IntLiteral, Builtin.IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
|
||||
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (Builtin.IntLiteral, Builtin.IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
|
||||
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (IntLiteral, IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
|
||||
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (IntLiteral, IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
|
||||
| (Int, Int, Int, Int, Int) | getName: | (Int, Int, Int, Int, Int) | getCanonicalType: | (Int, Int, Int, Int, Int) | getNumberOfTypes: | 5 |
|
||||
| (Int, String, Double) | getName: | (Int, String, Double) | getCanonicalType: | (Int, String, Double) | getNumberOfTypes: | 3 |
|
||||
| (Int, s: String, Double) | getName: | (Int, s: String, Double) | getCanonicalType: | (Int, s: String, Double) | getNumberOfTypes: | 3 |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user