From b57e92164ce7a8d8ddbc24875512879679984fe5 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Feb 2026 15:56:14 +0000 Subject: [PATCH] Python: Add declares/getAttribute API These could arguably be moved to `Class` itself, but for now I'm choosing to limit the changes to the `DuckTyping` module (until we decide on a proper API). --- .../dataflow/new/internal/DataFlowDispatch.qll | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 529fc3c95fd..2aaa33de159 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -2055,6 +2055,23 @@ module DuckTyping { hasMethod(cls, "__delete__") } + /** + * Holds if `cls` directly assigns to an attribute named `name` in its class body. + * This covers attribute assignments like `x = value`, but not method definitions. + */ + predicate declaresAttribute(Class cls, string name) { exists(getAnAttributeValue(cls, name)) } + + /** + * Gets the value expression assigned to attribute `name` directly in the class body of `cls`. + */ + Expr getAnAttributeValue(Class cls, string name) { + exists(Assign a | + a.getScope() = cls and + a.getATarget().(Name).getId() = name and + result = a.getValue() + ) + } + /** * Holds if `cls` is callable, i.e. it declares `__call__`. */