diff --git a/python/ql/lib/change-notes/2025-06-04-call-graph-type-annotations.md b/python/ql/lib/change-notes/2025-06-04-call-graph-type-annotations.md new file mode 100644 index 000000000000..2aa17e576326 --- /dev/null +++ b/python/ql/lib/change-notes/2025-06-04-call-graph-type-annotations.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Type annotations such as `foo : Bar` are now treated by the call graph as an indication that `foo` may be an instance of `Bar`. diff --git a/python/ql/lib/semmle/python/Exprs.qll b/python/ql/lib/semmle/python/Exprs.qll index accc370481aa..9e00e4f794b3 100644 --- a/python/ql/lib/semmle/python/Exprs.qll +++ b/python/ql/lib/semmle/python/Exprs.qll @@ -762,6 +762,17 @@ class Annotation extends Expr { or this = any(FunctionExpr f).getReturns() } + + /** Gets the expression that this annotation annotates. */ + Expr getAnnotatedExpression() { + result = any(AnnAssign a | a.getAnnotation() = this).getTarget() + or + result = any(Parameter p | p.getAnnotation() = this) + or + exists(FunctionExpr f | + this = f.getReturns() and result = f.getInnerScope().getReturnNode().getNode() + ) + } } /* Expression Contexts */ 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 1a38593bce48..781023a9658b 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -580,6 +580,11 @@ private module TrackClassInstanceInput implements CallGraphConstruction::Simple: class State = Class; predicate start(Node start, Class cls) { + exists(Annotation ann | + ann = classTracker(cls).asExpr() and + start.asExpr() = ann.getAnnotatedExpression() + ) + or resolveClassCall(start.(CallCfgNode).asCfgNode(), cls) or // result of `super().__new__` as used in a `__new__` method implementation