[`flake8-type-checking`] Avoid `strict` behavior when `future-annotations` are enabled (`TC001`, `TC002`, `TC003`) (#25035)
## Summary
Fixes #23185.
Enabling `lint.future-annotations = true` was silently flipping
`TC001`/`TC002`/`TC003` into strict-mode behaviour, even when
`lint.flake8-type-checking.strict = false`. The two settings are
orthogonal — the former controls whether `from __future__ import
annotations` is auto-inserted as a fix; the latter controls whether
typing-only imports whose module is already imported at runtime should
be flagged.
## Root cause
In
[`crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs),
the skip condition for implicit runtime imports read:
```rust
if !checker.settings().future_annotations
&& !checker.settings().flake8_type_checking.strict
&& runtime_imports.iter().any(|import| is_implicit_import(binding, import))
{
continue;
}
```
This required **both** `!future_annotations` **and** `!strict` to be
true. The moment a user opted into `future_annotations = true`, the
first clause became false and the entire skip was bypassed — making the
rules behave as if `strict = true`, regardless of the user's explicit
`strict = false` setting.
This also matches the direction @ntBre suggested on the issue:
> This does sound like a bug to me. I think I may have gotten this logic
wrong, and it should be an `or` instead of `and`…
## Solution
Drop the `!future_annotations` clause. Only `!strict` should gate
whether implicit runtime imports are skipped. The `future_annotations`
setting is handled elsewhere (as part of the fix-generation path for
adding the `__future__` import); it has no bearing on which imports are
flagged.
## Testing
- **New regression test** in
`crates/ruff_linter/src/rules/flake8_type_checking/mod.rs`:
`future_annotations_respects_non_strict_mode`, backed by a new fixture
`crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TC001-3_future_strict.py`.
The test pins the exact scenario from the issue
(`future_annotations=true`, `strict=false`, typing-only import whose
module has a runtime-used sibling) and asserts zero
`TC001`/`TC002`/`TC003` diagnostics are produced — a result that would
have been non-zero prior to this patch.
- All 92 pre-existing `flake8_type_checking` lib tests still pass
locally; the `add_future_import` snapshot tests (which exercise the
`future_annotations = true` path) also pass unchanged, confirming the
fix does not regress the auto-insert-`__future__`-import behaviour.
- `cargo test --lib flake8_type_checking` → 92 passed, 0 failed
- `cargo test --lib add_future_import` → 12 passed, 0 failed
## Checklist
- [x] Fixes the root cause (not just the symptom)
- [x] New test covers the exact failing scenario from the issue
- [x] All existing tests pass
- [x] No unrelated changes
- [x] Code style matches project conventions
- [x] Followed CONTRIBUTING.md