mirror of
https://github.com/github/codeql.git
synced 2026-06-03 04:40:14 +02:00
The new SSA's implicit entry-def predicate previously placed entries in
the variable's defining scope. For closure variables that's the outer
function, so inner functions had no entry def for the captured
variable — reads in the inner scope failed to resolve to any
definition.
Mirrors legacy ESSA's 'NonLocalVariable.getScopeEntryDefinition()':
place an implicit entry def at every reading scope's entry block,
independently of where the variable is *defined*. A closure variable
accessed in two nested functions and the outer one gets three entry
defs (one per reading scope).
Also makes 'ScopeEntryDefinition' extend 'EssaNodeDefinition' (matching
legacy ESSA), with 'getDefiningNode()' returning the scope's entry CFG
node. This requires extending the private 'writeDefNode' helper to
project i=-1 entries to bb.getNode(0).
Updates the new-vs-legacy comparison snapshot: closure-variable reads
('x:32:5'), nested global reads ('GLOBAL:52:1') now resolve. New
'def-only-new' entries appear for unbound names ('sum', 'open',
'compute') — the new SSA uniformly creates scope-entry defs for all
non-local reads, including those that legacy ESSA classifies as
builtin and excludes. This is a more uniform semantic and arguably
cleaner.
Updates the SsaTest 'some_undefined' annotation: previously documented
as a known limitation, now correctly resolves to a scope-entry def.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
41 lines
928 B
Python
41 lines
928 B
Python
# Basic SSA tests for the new-CFG SSA adapter.
|
|
#
|
|
# The shared SSA implementation prunes its construction by liveness:
|
|
# definitions of variables that are not read are never materialised.
|
|
# This is by design — write-only variables would only bloat the SSA
|
|
# graph. Tests therefore must always include a read of each variable
|
|
# being verified.
|
|
#
|
|
# Annotations:
|
|
# def=<var>: there is an SSA write definition of <var> at this line
|
|
# use=<var>: <var> is used here and the read resolves to some def
|
|
|
|
|
|
def basic_param(x): # $ def=x
|
|
return x # $ use=x
|
|
|
|
|
|
def basic_assign():
|
|
y = 1 # $ def=y
|
|
return y # $ use=y
|
|
|
|
|
|
def reassignment():
|
|
x = 1
|
|
x = 2 # $ def=x
|
|
return x # $ use=x
|
|
|
|
|
|
def if_else_phi(cond): # $ def=cond
|
|
if cond: # $ use=cond phi=x
|
|
x = 1 # $ def=x
|
|
else:
|
|
x = 2 # $ def=x
|
|
return x # $ use=x
|
|
|
|
|
|
def use_global():
|
|
return some_undefined # $ use=some_undefined
|
|
|
|
|