510 Commits

Author SHA1 Message Date
Charlie Marsh 5f040fab4b [ty] Expand support for narrowing within walruses (#24968)
## Summary

Support narrowing for a few more already-supported sites, but in the
context of a walrus, as in:

```python
def f(t: tuple[int, int] | tuple[None, None]):
    if (first := t[0]) is not None:
        reveal_type(first)  # int
        reveal_type(t)      # tuple[int, int]
    else:
        reveal_type(first)  # None
        reveal_type(t)      # tuple[None, None]
```
2026-05-01 23:09:36 -04:00
Eyüp Can Akman f32733c063 [flake8-pyi] Fix PYI016 false positive for f-string debug specifier (#24098)
Co-authored-by: Micha Reiser <micha@reiser.io>
2026-04-30 11:14:25 +02:00
Alex Waygood e89f8ef295 [ty] Fix bad diagnostic range for incorrect implicit __init_subclass__ calls (#24541) 2026-04-10 14:27:13 +00:00
Charlie Marsh 2ad94df0e1 [ty] Add a SupportedPythonVersion enum (#24412)
## Summary

This unifies the validation of supported Python versions between the CLI
and TOML (e.g., `environment.python-version`) by introducing a single
enum to share across them.
2026-04-10 13:48:48 +00:00
Charlie Marsh d52c080447 [ty] Ignore unsupported editor-selected Python versions (#24498)
## Summary

Like https://github.com/astral-sh/ruff/pull/24402, we want to ignore
unsupported Python versions that come from the editor. Instead, we'll
fall back to the default version (if there's no other configuration
set).

One nuance here is that we don't actively show the user a popup if we
ignore this version; we just use `tracing::warn!("{message}")`. It seems
undesirable to show a popup at the conversion site, since we'd then be
showing it even if the fallback version were never used. Is it desirable
to show a popup _ever_?
2026-04-10 09:35:04 -04:00
Micha Reiser 24083af5a9 Rename patterns and arguments source order iterator method (#24532) 2026-04-10 07:32:15 +00:00
Anish Giri 87a0f01cfd [ruff] Treat f-string interpolation as potential side effect in RUF019 (#24426)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
- Does this PR follow our AI policy
(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?
-->

## Summary

Fixes #12953
                                                            
F-string interpolation can call `__format__`/`__str__`/`__repr__`, which
may have side effects. RUF019 was applying a safe auto-fix that
collapsed two `__str__` calls into one, changing behavior.
Added a tri-state `SideEffect` enum (`No`/`Maybe`/`Yes`) and a
`side_effect()` function that reuses the existing `any_over_expr`
traversal via a new `FnMut` variant (`any_over_expr_mut`), following the
approach suggested by @ntBre in the other closed pr tagged in the issue

In RUF019, `SideEffect::Maybe` (non-literal f-string interpolation) now
produces an unsafe fix instead of a safe one. Literal interpolations
like `f"{1}"` remain safe.

## Test Plan

- Added f-string fixture cases to `RUF019.py` (non-literal → unsafe,
literal → safe, no interpolation → safe).
- `cargo nextest run -p ruff_linter`
- Ecosystem check (stable + preview)
2026-04-09 09:36:17 +02:00
Charlie Marsh 55f667ba39 Convert Clippy allows to expects (#24473) 2026-04-07 14:10:43 -04:00
Micha Reiser 3ffa53af20 Use dyn dispatch internally only for any_over methods (#24468) 2026-04-07 13:58:54 +02:00
Alex Waygood 298d1cebce [ty] Tighten up validation of subscripts and attributes in type expressions (#24329) 2026-04-01 20:01:20 +01:00
Denys Zhak 4d963140af [ty] report unused bindings as unnecessary hint diagnostics (#23305)
Co-authored-by: Micha Reiser <micha@reiser.io>
2026-03-27 11:06:06 +01:00
Daniil Sivak ceddca7123 Implement useless-finally (RUF-072) (#24165)
Closes #19158

Implements the `useless_finally` rule (`RUF072`), which detects useless
`finally` blocks that only contain `pass` or `...`.

It handles two cases:
- `try/except/finally: pass` - the `finally` clause is removed, leaving
a valid `try/except`
- bare `try/finally: pass`: the entire `try/finally` is unwrapped, the
try body is dedented to replace the whole statement

Fix is skipped when comments are present in or around the `finally`
block.

It complements with existing rules like `RUF047` (`needless-else`) and
`SIM105` (`suppressible-exception`). It case of `SIM105` it also
unblocks this rule, as currently `SIM105` got skipped if `finally` has
any body at all (even just `pass`).

## Test Plan

- `RUF072.py` - main rule test with error cases and non-error.
- `useless_finally_and_needless_else` - test function, which checks how
`RUF047` and `RUF072` work together on the same `try` statement.
- `useless_finally_and_suppressible_exception` - test function, which
checks how `RUF072` and `SIM105` work together.
2026-03-25 13:06:53 +01:00
Ibraheem Ahmed fbd7b8ab44 [ty] Narrow keyword arguments when unpacking dictionary instances (#23436)
Extends https://github.com/astral-sh/ruff/pull/22882 to use narrowed
keys when inferring splatted dictionary expressions.

Resolves https://github.com/astral-sh/ty/issues/1248.
2026-03-16 22:33:24 -04:00
Alex Waygood 0d84a0654a [ty] Add validation for type parameters with defaults after TypeVarTuple (#23807) 2026-03-08 18:28:16 +00:00
Charlie Marsh 8a10b6743c Add support for lazy import parsing (#23755)
## Summary

This PR adds `is_lazy` to the AST and parser, and supports `lazy` import
formatting in the formatter. The name `is_lazy` matches the [CPython
AST](https://github.com/python/cpython/blob/c3fb0d9d96902774c08b199dda0479a8d31398a5/Parser/Python.asdl#L48),
though we could modify it if there's demand.

There are no further changes to Ruff rules or even to Ruff import
sorting, since those deserve separate design discussions.

I've also omitted support for flagging semantic syntax errors (e.g.,
`lazy import` inside `def`).

See: https://github.com/astral-sh/ruff/issues/21305.
2026-03-06 09:12:21 -05:00
Amethyst Reese c2eb311ec3 Drop explicit support for .qmd file extension (#23572) 2026-02-25 15:58:29 -08:00
Charlie Marsh 8fb1123c54 [ty] Visit parameters in source order (#23403)
## Summary

Previously, `Parameters::visit_source_order` used `ParametersIterator`
which visits in type-based order. For well-formed Python code, this
matches source order; but error recovery can produce ASTs like `def
foo(**kwargs, a):`.
2026-02-19 02:46:06 +00:00
Charlie Marsh c4c1f542e0 [ty] Visit pattern arguments in source order (#23398)
## Summary

`PatternArguments::visit_source_order()` visited all positional patterns
first, then all keyword patterns. But keyword patterns can appear before
positional patterns in the source text:

```python
case ast.Attribute(value=ast.Name(id), attr)
```

The visitor would process `attr` first, then `value=ast.Name(id)`.

We now visit in source order using the pattern established via
`arguments_source_order`.

Closes https://github.com/astral-sh/ty/issues/2417.
2026-02-18 13:52:12 -05:00
Amethyst Reese 8fc595aa6b Support markdown in extension mappings (#23218) 2026-02-11 10:36:05 -08:00
Thibault Vatter ddd192ac18 Support Quarto Markdown language markers (#22947)
See [this
comment](https://github.com/astral-sh/ruff/pull/22470#issuecomment-3818540650)
from #22470. Also related to #6140.

## Summary

Add support for formatting code blocks with curly brace syntax (e.g.,
\`\`\`{python}, \`\`\`{py}, \`\`\`{pyi}) in Markdown files. This syntax
is commonly used in tools like Quarto and R Markdown.

The regex pattern now matches both the standard syntax (\`\`\`python)
and the curly brace variant (\`\`\`{python}).

## Test Plan

Added test cases.

Fix #22951

---------

Co-authored-by: Amethyst Reese <amethyst@n7.gg>
2026-02-04 16:21:34 -08:00
Aria Desires 2c902c7c68 [ty] fix bug in string annotations and clean up diagnostics (#22913)
This multiplication was always using only using the larger sub-ast size
value, which didn't do anything unsound but made sub-string annotations
become exhausted on files 32x smaller than expected (16k nodes instead
of 512k nodes).

Also I decided to do a cleanup pass on the diagnostics to make them more
precise and helpful.
2026-01-28 10:55:35 -05:00
Aria Desires 45acbd0b66 [ty] Improve support for goto-type, goto-declaration, hover, and highlighting of string annotations (#22878)
I ultimately hunted down the core issue with my previous approach to "if
you give string annotation sub-AST nodes any kind of NodeIndex, a bunch
of random places in the code will start thinking it's ok to store info
about them, which is a problem because they all count up from index 0
which creates conflicts/crashes".

Rather than trying to play whackamole I decided to create a scheme to
give string annotation nodes a unique NodeIndex by shifting up the
parent node's index -- so 0xAB's sub-AST nodes look like 0xAB00...0000,
0xAB00..0001, etc. This scheme avoids any collisions for any reasonable
AST (most string annotations are like, a dozen sub-nodes, so they need
maybe 4 or 5 bits, which would require hundreds of MB of python code to
run out of bits...).

As a bonus, this admits an extremely simple implementation of recording
and fetching sub-AST types... they just are stored now, and you can just
pass in their NodeIndex and get back actual results.

* Fixes https://github.com/astral-sh/ty/issues/1640
* Fixes https://github.com/astral-sh/ty/issues/2028
2026-01-27 23:26:40 -05:00
Amethyst Reese ffa07b57f8 Apply formatting to markdown code blocks (#22470)
Adds initial support for formatting Python code blocks inside Markdown
files.

- Adds `Markdown` source types/kinds
- Maps `.md` file extension to `Markdown` by default
- Uses simple regex adapted from blacken-docs to find and format fenced
python code blocks
- Dedents contents before formatting, and reapplies indent from fenced
<code>```py</code> header
- Selects `Python` vs `Stub` options based on language label on code
block
- Silently skips formatting for any code block with syntax errors or
that produce formatting errors.
- CLI tests formatting via both stdin and from filesystem
- Requires running with `--preview`, and otherwise emits formatting
error when given a markdown file
- Requires a user to `extend-include = ["**/*.md"]` if they want to
format markdown files by default

Limitations:

- Returns a formatting error if run with a range of any sort
- Ignores implicit code blocks (no code fence)
- Doesn't yet support `~~~` fences, arbitrary fence lengths, or code
blocks inside blockquotes

Issue #3792
2026-01-27 17:26:09 -08:00
Amethyst Reese c696ef4025 Skip walking all tokens when loading range suppressions (#22446)
- Adds `Tokens::split_at()` to get tokens before/after an offset.
- Updates `Suppressions::load_from_tokens` to take an `Indexer` and use
comment ranges to minimize the need for walking tokens looking for
indent/dedent.

Adapted from
https://github.com/astral-sh/ruff/pull/21441#pullrequestreview-3503773083

Fixes #22087
2026-01-15 12:35:24 -08:00
Ibraheem Ahmed 8ac5f9d8bc [ty] Use key and value parameter types as type context for __setitem__ dunder calls (#22148)
## Summary

Resolves https://github.com/astral-sh/ty/issues/2136.
2026-01-12 16:05:05 -05:00
Jason K Hall 3b61da0da3 Allow Python 3.15 as valid target-version value in preview (#22419) 2026-01-07 09:38:36 +01:00
Matthew Mckee a2e0ff57c3 Run cargo sort (#22310) 2026-01-02 19:58:15 +00:00
Aria Desires 8d32ad1cab [ty] Add support for attribute docstrings (#22036)
## Summary

I should have factored this better but this includes a drive-by move of
find_node to ruff_python_ast so ty_python_semantic can use it too.

* Fixes https://github.com/astral-sh/ty/issues/2017 

## Test Plan

Snapshots galore
2025-12-18 12:18:20 +00:00
Denys Zhak 27912d46b1 Remove BackwardsTokenizer based parenthesized_range references in ruff_linter (#21836)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-11 13:04:57 +01:00
Micha Reiser dc2f0a86fd Include more details in Tokens 'offset is inside token' panic message (#21860) 2025-12-09 11:12:35 +01:00
Denys Zhak f4e4229683 Add token based parenthesized_ranges implementation (#21738)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-03 08:15:17 +00:00
Micha Reiser 515de2d062 Move Token, TokenKind and Tokens to ruff-python-ast (#21760) 2025-12-02 20:10:46 +01:00
Dan Parizher f052bd644c [flake8-simplify] Fix truthiness assumption for non-iterable arguments in tuple/list/set calls (SIM222, SIM223) (#21479)
## Summary

Fixes false positives in SIM222 and SIM223 where truthiness was
incorrectly assumed for `tuple(x)`, `list(x)`, `set(x)` when `x` is not
iterable.

Fixes #21473.

## Problem

`Truthiness::from_expr` recursively called itself on arguments to
iterable initializers (`tuple`, `list`, `set`) without checking if the
argument is iterable, causing false positives for cases like `tuple(0)
or True` and `tuple("") or True`.

## Approach

Added `is_definitely_not_iterable` helper and updated
`Truthiness::from_expr` to return `Unknown` for non-iterable arguments
(numbers, booleans, None) and string literals when called with iterable
initializers, preventing incorrect truthiness assumptions.

## Test Plan

Added test cases to `SIM222.py` and `SIM223.py` for `tuple("")`,
`tuple(0)`, `tuple(1)`, `tuple(False)`, and `tuple(None)` with `or True`
and `and False` patterns.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-12-01 16:57:51 -05:00
Micha Reiser aec225d825 [ty] Fix panic for unclosed string literal in type annotation position (#21592) 2025-11-23 16:51:58 +01:00
Dan Parizher 1fd852fb3f [ruff] Ignore str() when not used for simple conversion (RUF065) (#21330)
## Summary

Fixed RUF065 (`logging-eager-conversion`) to only flag `str()` calls
when they perform a simple conversion that can be safely removed. The
rule now ignores `str()` calls with no arguments, multiple arguments,
starred arguments, or keyword unpacking, preventing false positives.

Fixes #21315

## Problem Analysis

The RUF065 rule was incorrectly flagging all `str()` calls in logging
statements, even when `str()` was performing actual conversion work
beyond simple type coercion. Specifically, the rule flagged:

- `str()` with no arguments - which returns an empty string
- `str(b"data", "utf-8")` with multiple arguments - which performs
encoding conversion
- `str(*args)` with starred arguments - which unpacks arguments
- `str(**kwargs)` with keyword unpacking - which passes keyword
arguments

These cases cannot be safely removed because `str()` is doing meaningful
work (encoding conversion, argument unpacking, etc.), not just redundant
type conversion.

The root cause was that the rule only checked if the function was
`str()` without validating the call signature. It didn't distinguish
between simple `str(value)` conversions (which can be removed) and more
complex `str()` calls that perform actual work.

## Approach

The fix adds validation to the `str()` detection logic in
`logging_eager_conversion.rs`:

1. **Check argument count**: Only flag `str()` calls with exactly one
positional argument (`str_call_args.args.len() == 1`)
2. **Check for starred arguments**: Ensure the single argument is not
starred (`!str_call_args.args[0].is_starred_expr()`)
3. **Check for keyword arguments**: Ensure there are no keyword
arguments (`str_call_args.keywords.is_empty()`)

This ensures the rule only flags cases like `str(value)` where `str()`
is truly redundant and can be removed, while ignoring cases where
`str()` performs actual conversion work.

The fix maintains backward compatibility - all existing valid test cases
continue to be flagged correctly, while the new edge cases are properly
ignored.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-11-10 18:04:41 -05:00
Dan Parizher 04e7cecab3 [flake8-simplify] Fix SIM222 false positive for tuple(generator) or None (SIM222) (#21187)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-11-10 14:27:31 +01:00
chiri b93d8f2b9f [refurb] Preserve argument ordering in autofix (FURB103) (#20790)
Fixes https://github.com/astral-sh/ruff/issues/20785
2025-10-31 11:16:09 -04:00
Takayuki Maeda 2c9433796a [ruff] Autogenerate TypeParam nodes (#21028) 2025-10-22 14:06:24 +02:00
Takayuki Maeda 6271fba1e1 [ruff] Auto generate ast Pattern nodes (#21024) 2025-10-22 08:24:34 +02:00
Takayuki Maeda 48b50128eb [ruff] Update schemars to v1 (#20942) 2025-10-20 08:59:52 +02:00
Micha Reiser 4fc7dd300c Improved error recovery for unclosed strings (including f- and t-strings) (#20848) 2025-10-15 09:50:56 +02:00
Dan Parizher c69fa75cd5 Fix false negatives in Truthiness::from_expr for lambdas, generators, and f-strings (#20704) 2025-10-14 03:06:17 -05:00
David Peter 1f1542db51 [ty] Use 3.14 as the default version (#20759)
## Summary

Bump the latest supported Python version of ty to 3.14 and updates some
references from 3.13 to 3.14.

This also fixes a bug with `dataclasses.field` on 3.14 (which adds a new
keyword-only parameter to that function, breaking our previously naive
matching on the parameter structure of that function).

## Test Plan

A `ty check` on a file with template strings (without any further
configuration) doesn't raise errors anymore.
2025-10-08 11:38:47 +02:00
Brent Westbrook 88c0ce3e38 Update default and latest Python versions for 3.14 (#20725)
Summary
--

Closes #19467 and also removes the warning about using Python 3.14
without
preview enabled.

I also bumped `PythonVersion::default` to 3.9 because it reaches EOL
this month,
but we could also defer that for now if we wanted.

The first three commits are related to the `latest` bump to 3.14; the
fourth commit
bumps the default to 3.10.

Note that this PR also bumps the default Python version for ty to 3.10
because
there was a test asserting that it stays in sync with
`ast::PythonVersion`.

Test Plan
--

Existing tests

I spot-checked the ecosystem report, and I believe these are all
expected. Inbits doesn't specify a target Python version, so I guess
we're applying the default. UP007, UP035, and UP045 all use the new
default value to emit new diagnostics.
2025-10-07 12:23:11 -04:00
David Peter 0092794302 [ty] Use typing.Self for the first parameter of instance methods (#20517)
## Summary

Modify the (external) signature of instance methods such that the first
parameter uses `Self` unless it is explicitly annotated. This allows us
to correctly type-check more code, and allows us to infer correct return
types for many functions that return `Self`. For example:

```py
from pathlib import Path
from datetime import datetime, timedelta

reveal_type(Path(".config") / ".ty")  # now Path, previously Unknown

def _(dt: datetime, delta: timedelta):
    reveal_type(dt - delta)  # now datetime, previously Unknown
```

part of https://github.com/astral-sh/ty/issues/159

## Performance

I ran benchmarks locally on `attrs`, `freqtrade` and `colour`, the
projects with the largest regressions on CodSpeed. I see much smaller
effects locally, but can definitely reproduce the regression on `attrs`.
From looking at the profiling results (on Codspeed), it seems that we
simply do more type inference work, which seems plausible, given that we
now understand much more return types (of many stdlib functions). In
particular, whenever a function uses an implicit `self` and returns
`Self` (without mentioning `Self` anywhere else in its signature), we
will now infer the correct type, whereas we would previously return
`Unknown`. This also means that we need to invoke the generics solver in
more cases. Comparing half a million lines of log output on attrs, I can
see that we do 5% more "work" (number of lines in the log), and have a
lot more `apply_specialization` events (7108 vs 4304). On freqtrade, I
see similar numbers for `apply_specialization` (11360 vs 5138 calls).
Given these results, I'm not sure if it's generally worth doing more
performance work, especially since none of the code modifications
themselves seem to be likely candidates for regressions.

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/attrs` | 92.6 ± 3.6 | 85.9 |
102.6 | 1.00 |
| `./ty_self check /home/shark/ecosystem/attrs` | 101.7 ± 3.5 | 96.9 |
113.8 | 1.10 ± 0.06 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/freqtrade` | 599.0 ± 20.2 |
568.2 | 627.5 | 1.00 |
| `./ty_self check /home/shark/ecosystem/freqtrade` | 607.9 ± 11.5 |
594.9 | 626.4 | 1.01 ± 0.04 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/colour` | 423.9 ± 17.9 | 394.6
| 447.4 | 1.00 |
| `./ty_self check /home/shark/ecosystem/colour` | 426.9 ± 24.9 | 373.8
| 456.6 | 1.01 ± 0.07 |

## Test Plan

New Markdown tests

## Ecosystem report

* apprise: ~300 new diagnostics related to problematic stubs in apprise
😩
* attrs: a new true positive, since [this
function](https://github.com/python-attrs/attrs/blob/4e2c89c82398914dbbe24fe00b03f00a8b8efe05/tests/test_make.py#L2135)
is missing a `@staticmethod`?
* Some legitimate true positives
* sympy: lots of new `invalid-operator` false positives in [matrix
multiplication](https://github.com/sympy/sympy/blob/cf9f4b680520821b31e2baceb4245252939306be/sympy/matrices/matrixbase.py#L3267-L3269)
due to our limited understanding of [generic `Callable[[Callable[[T1,
T2], T3]], Callable[[T1, T2], T3]]` "identity"
types](https://github.com/sympy/sympy/blob/cf9f4b680520821b31e2baceb4245252939306be/sympy/core/decorators.py#L83-L84)
of decorators. This is not related to type-of-self.

## Typing conformance results

The changes are all correct, except for
```diff
+generics_self_usage.py:50:5: error[invalid-assignment] Object of type `def foo(self) -> int` is not assignable to `(typing.Self, /) -> int`
```
which is related to an assignability problem involving type variables on
both sides:
```py
class CallableAttribute:
    def foo(self) -> int:
        return 0

    bar: Callable[[Self], int] = foo  # <- we currently error on this assignment
```

---------

Co-authored-by: Shaygan Hooshyari <sh.hooshyari@gmail.com>
2025-09-29 21:08:08 +02:00
Amethyst Reese 83f80effec include .pyw files by default when linting and formatting (#20458)
- Adds test cases exercising file selection by extension with
`--preview` enabled and disabled.
- Adds `INCLUDE_PREVIEW` with file patterns including `*.pyw`.
- In global preview mode, default configuration selects patterns from
`INCLUDE_PREVIEW`.
- Manually tested ruff server with local vscode for both formatting and
linting of a `.pyw` file.

Closes https://github.com/astral-sh/ruff/issues/13246
2025-09-24 08:39:30 -07:00
Dylan b6bd32d9dc Track t-strings and f-strings for token-based rules and suppression comments (#20357)
Our token-based rules and `noqa` extraction used an `Indexer` that kept
track of f-string ranges but not t-strings. We've updated the `Indexer`
and downstream uses thereof to handle both f-strings and t-strings.

Most of the diff is renaming and adding tests.

Note that much of the "new" logic gets to be naive because the lexer has
already ensured that f and t-string "starts" are paired with their
respective "ends", even amidst nesting and so on.

Finally: one could imagine wanting to know if a given interpolated
string range corresponds to an f-string or a t-string, but I didn't find
a place where we actually needed this.

Closes #20310
2025-09-12 13:00:12 -05:00
Dan Parizher 4c64ba4ee1 [flake8-bandit] Fix truthiness: dict-only ** displays not truthy for shell (S602, S604, S609) (#20177)
## Summary
Fixes #19927
2025-09-10 17:06:33 -04:00
Alex Waygood 5d52902e18 [ty] Implement the legacy PEP-484 convention for indicating positional-only parameters (#20248)
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-05 17:56:06 +01:00
Dylan 694e7ed52e Less confidently mark f-strings as empty when inferring truthiness (#20152)
When computing the boolean value of an f-string, we over-eagerly
interpreted some f-string interpolations as empty. In this PR we now
mark the truthiness of f-strings involving format specs, debug text, and
bytes literals as "unknown".

This will probably result in some false negatives, which may be further
refined (for example - there are probably many cases where
`is_not_empty_f_string` should be modified to return `true`), but for
now at least we should have fewer false positives.

Affected rules (may not be an exhaustive list):

- [unnecessary-empty-iterable-within-deque-call
(RUF037)](https://docs.astral.sh/ruff/rules/unnecessary-empty-iterable-within-deque-call/#unnecessary-empty-iterable-within-deque-call-ruf037)
- [falsy-dict-get-fallback
(RUF056)](https://docs.astral.sh/ruff/rules/falsy-dict-get-fallback/#falsy-dict-get-fallback-ruf056)
- [pytest-assert-always-false
(PT015)](https://docs.astral.sh/ruff/rules/pytest-assert-always-false/#pytest-assert-always-false-pt015)
- [expr-or-not-expr
(SIM221)](https://docs.astral.sh/ruff/rules/expr-or-not-expr/#expr-or-not-expr-sim221)
- [expr-or-true
(SIM222)](https://docs.astral.sh/ruff/rules/expr-or-true/#expr-or-true-sim222)
- [expr-and-false
(SIM223)](https://docs.astral.sh/ruff/rules/expr-and-false/#expr-and-false-sim223)

Closes #19935
2025-08-29 22:12:54 +00:00