Files
codeql/python/ql/lib/semmle/python/SelfAttribute.qll
Copilot 717ff62d70 Python: deprecate AstNode.getAFlowNode() and rewrite internal callers
Preparatory refactor for the shared-CFG dataflow migration.

Deprecates the AstNode.getAFlowNode() cached predicate on the public
Python QL API and rewrites all ~140 internal callers across lib/, src/,
test/, and tools/ from `expr.getAFlowNode() = cfgNode` to
`cfgNode.getNode() = expr`, using ControlFlowNode.getNode() which
already exists in Flow.qll.

The predicate itself is preserved (with a deprecation note pointing at
the new pattern) so external users do not experience churn — they can
migrate at their own pace and the AST/CFG hierarchies still get the
intended untangling once the deprecation eventually elapses.

Semantic noop verified by:
- All 361 lib/ + src/ queries compile clean.
- All 122 ControlFlow + PointsTo library-tests pass.
- All 64 dataflow library-tests pass.
- All 113 Variables/Exceptions/Expressions/Statements/Functions/Imports/
  Security/CWE-798/ModificationOfParameterWithDefault query-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-22 14:55:19 +02:00

95 lines
2.9 KiB
Plaintext

/**
* Utilities to support queries about instance attribute accesses of
* the form `self.attr`.
*/
import python
private import semmle.python.pointsto.Filters
private import LegacyPointsTo
/**
* An attribute access where the left hand side of the attribute expression
* is `self`.
*/
class SelfAttribute extends Attribute {
SelfAttribute() { self_attribute(this, _) }
Class getClass() { self_attribute(this, result) }
}
/** Whether variable 'self' is the self variable in method 'method' */
private predicate self_variable(Function method, Variable self) {
self.isParameter() and
method.isMethod() and
method.getArg(0).asName() = self.getAnAccess()
}
/** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */
private predicate self_attribute(Attribute attr, Class cls) {
exists(Function f, Variable self | self_variable(f, self) |
self.getAnAccess() = attr.getObject() and
cls = f.getScope+()
)
}
/** A helper class for UndefinedClassAttribute.ql &amp; MaybeUndefinedClassAttribute.ql */
class SelfAttributeRead extends SelfAttribute {
SelfAttributeRead() {
this.getCtx() instanceof Load and
// Be stricter for loads.
// We want to generous as to what is defined (i.e. stores),
// but strict as to what needs to be defined (i.e. loads).
exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func |
func.getFunction() = this.getScope() and
cls.getPyClass() = this.getClass()
)
}
predicate guardedByHasattr() {
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
this_.getNode() = this and obj_.getNode() = this.getObject()
|
var.getAUse() = obj_ and
hasattr(n, var.getAUse(), this.getName()) and
n.strictlyDominates(this_)
)
}
pragma[noinline]
predicate locallyDefined() {
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
store_.getNode() = store and this_.getNode() = this
|
this.getName() = store.getName() and
this.getScope() = store.getScope() and
store_.strictlyDominates(this_)
)
}
}
class SelfAttributeStore extends SelfAttribute {
SelfAttributeStore() { this.getCtx() instanceof Store }
Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) }
}
private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) {
exists(SsaVariable param |
method.getFunction().getArg(n).asName() = param.getDefinition().getNode()
|
exists(AttrNode attr |
attr.getObject(name) = param.getAUse() and
attr.isStore()
)
or
exists(FunctionObject callee, int m |
callee.getArgumentForCall(_, m) = param.getAUse() and
attr_assigned_in_method_arg_n(callee, name, m)
)
)
}
predicate attribute_assigned_in_method(FunctionObject method, string name) {
attr_assigned_in_method_arg_n(method, name, 0)
}