mirror of
https://github.com/github/codeql.git
synced 2026-05-27 17:41:24 +02:00
Option 2: eliminates the AST→CFG bridge from the AST layer. Previously
'AstNode.getAFlowNode()' returned a 'ControlFlowNode' from the legacy
'Flow.qll' CFG via 'py_flow_bb_node' — this hardcoded the AST to know
about the legacy CFG, preventing files from cleanly switching to the
new shared CFG.
Removes:
* 'AstNode.getAFlowNode()' from 'AstExtended.qll'
* Type-narrowing overrides on 'Attribute' / 'Subscript' / 'Call' /
'IfExp' / 'Name' / 'NameConstant' / 'ImportMember' (in Exprs.qll
and Import.qll)
Rewrites ~130 call sites across 'python/ql/lib/' and 'python/ql/src/'
to bridge from the CFG side instead:
Before: node = expr.getAFlowNode()
After: node.getNode() = expr
Before: expr.getAFlowNode().(DefinitionNode).getValue()
After: exists(DefinitionNode d | d.getNode() = expr | d.getValue())
Before: cn.operands(const.getAFlowNode(), op, x)
After: exists(ControlFlowNode c | c.getNode() = const | cn.operands(c, op, x))
This is semantically a no-op — both forms are duals of the same predicate.
Verified by passing all library tests:
* 64 dataflow tests
* 28 ControlFlow + dataflow-new-ssa tests
* 1 essa SSA-compute test
* 93 tests total in the focused suite
Once committed, files that want to switch from the legacy 'Flow' CFG
to the new 'Cfg' facade only need to change their imports — the
bridge sites are CFG-side and respect whichever ControlFlowNode is in
scope.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
39 lines
1.4 KiB
Plaintext
39 lines
1.4 KiB
Plaintext
/**
|
|
* @name Result of integer division may be truncated
|
|
* @description The arguments to a division statement may be integers, which
|
|
* may cause the result to be truncated in Python 2.
|
|
* @kind problem
|
|
* @tags maintainability
|
|
* correctness
|
|
* @problem.severity warning
|
|
* @sub-severity high
|
|
* @precision very-high
|
|
* @id py/truncated-division
|
|
*/
|
|
|
|
import python
|
|
private import LegacyPointsTo
|
|
|
|
from BinaryExpr div, ControlFlowNode left, ControlFlowNode right
|
|
where
|
|
// Only relevant for Python 2, as all later versions implement true division
|
|
major_version() = 2 and
|
|
exists(BinaryExprNode bin, Value lval, Value rval |
|
|
bin.getNode() = div and
|
|
bin.getNode().getOp() instanceof Div and
|
|
bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and
|
|
lval.getClass() = ClassValue::int_() and
|
|
bin.getRight().(ControlFlowNodeWithPointsTo).pointsTo(rval, right) and
|
|
rval.getClass() = ClassValue::int_() and
|
|
// Ignore instances where integer division leaves no remainder
|
|
not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and
|
|
not bin.getNode().getEnclosingModule().hasFromFuture("division") and
|
|
// Filter out results wrapped in `int(...)`
|
|
not exists(CallNode c |
|
|
c = ClassValue::int_().getACall() and
|
|
c.getAnArg() = bin
|
|
)
|
|
)
|
|
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
|
|
left, "left", right, "right"
|