diff --git a/python/ql/lib/change-notes/2026-11-05-await-side-effects.md b/python/ql/lib/change-notes/2026-11-05-await-side-effects.md new file mode 100644 index 000000000000..9f84762e711d --- /dev/null +++ b/python/ql/lib/change-notes/2026-11-05-await-side-effects.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Await expressions are now treated as having side effects. This prevents false positives from the "Statement has no effect" query (`py/ineffectual-statement`) when an awaited task or coroutine's result is discarded. diff --git a/python/ql/lib/semmle/python/Exprs.qll b/python/ql/lib/semmle/python/Exprs.qll index 937c9e5ffd79..1d4b545909ae 100644 --- a/python/ql/lib/semmle/python/Exprs.qll +++ b/python/ql/lib/semmle/python/Exprs.qll @@ -623,6 +623,8 @@ class None extends NameConstant { class Await extends Await_ { /* syntax: await Expr */ override Expr getASubExpression() { result = this.getValue() } + + override predicate hasSideEffects() { any() } } /** A formatted string literal expression, such as `f'hello {world!s}'` */ diff --git a/python/ql/test/query-tests/Statements/no_effect/await_task.py b/python/ql/test/query-tests/Statements/no_effect/await_task.py new file mode 100644 index 000000000000..6d464c954666 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/await_task.py @@ -0,0 +1,15 @@ +import asyncio + + +async def returns_value(): + return 42 + + +async def await_task_result(): + task = asyncio.create_task(returns_value()) + await task + + +async def await_coroutine_result(): + coroutine = returns_value() + await coroutine