Merge remote-tracking branch 'origin/main' into dcreager/callable-return

* origin/main: (36 commits)
  [ty] Defer all parameter and return type annotations (#21906)
  [ty] Fix workspace symbols to return members too (#21926)
  Document range suppressions, reorganize suppression docs (#21884)
  Ignore ruff:isort like ruff:noqa in new suppressions (#21922)
  [ty] Handle `Definition`s in `SemanticModel::scope` (#21919)
  [ty] Attach salsa db when running ide tests for easier debugging (#21917)
  [ty] Don't show hover for expressions with no inferred type (#21924)
  [ty] avoid unions of generic aliases of the same class in fixpoint (#21909)
  [ty] Squash false positive logs for failing to find `builtins` as a real module
  [ty] Uniformly use "not supported" in diagnostics (#21916)
  [ty] Reduce size of ty-ide snapshots (#21915)
  [ty] Adjust scope completions to use all reachable symbols
  [ty] Rename `all_members_of_scope` to `all_end_of_scope_members`
  [ty] Remove `all_` prefix from some routines on UseDefMap
  Enable `--document-private-items` for `ruff_python_formatter` (#21903)
  Remove `BackwardsTokenizer` based `parenthesized_range` references in `ruff_linter` (#21836)
  [ty] Revert "Do not infer types for invalid binary expressions in annotations" (#21914)
  Skip over trivia tokens after re-lexing (#21895)
  [ty] Avoid inferring types for invalid binary expressions in string annotations (#21911)
  [ty] Improve overload call resolution tracing (#21913)
  ...
This commit is contained in:
Douglas Creager 2025-12-11 16:05:24 -05:00
commit a892be3124
198 changed files with 5696 additions and 4351 deletions

View File

@ -298,7 +298,7 @@ jobs:
# sync, not just public items. Eventually we should do this for all # sync, not just public items. Eventually we should do this for all
# crates; for now add crates here as they are warning-clean to prevent # crates; for now add crates here as they are warning-clean to prevent
# regression. # regression.
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db --document-private-items - run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db -p ruff_python_formatter --document-private-items
env: env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025). # Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings" RUSTDOCFLAGS: "-D warnings"

View File

@ -47,6 +47,7 @@ jobs:
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with: with:
shared-key: "mypy-primer"
workspaces: "ruff" workspaces: "ruff"
- name: Install Rust toolchain - name: Install Rust toolchain
@ -86,6 +87,7 @@ jobs:
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with: with:
workspaces: "ruff" workspaces: "ruff"
shared-key: "mypy-primer"
- name: Install Rust toolchain - name: Install Rust toolchain
run: rustup show run: rustup show
@ -105,3 +107,54 @@ jobs:
with: with:
name: mypy_primer_memory_diff name: mypy_primer_memory_diff
path: mypy_primer_memory.diff path: mypy_primer_memory.diff
# Runs mypy twice against the same ty version to catch any non-deterministic behavior (ideally).
# The job is disabled for now because there are some non-deterministic diagnostics.
mypy_primer_same_revision:
name: Run mypy_primer on same revision
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
timeout-minutes: 20
# TODO: Enable once we fixed the non-deterministic diagnostics
if: false
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: ruff
fetch-depth: 0
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
workspaces: "ruff"
shared-key: "mypy-primer"
- name: Install Rust toolchain
run: rustup show
- name: Run determinism check
env:
BASE_REVISION: ${{ github.event.pull_request.head.sha }}
PRIMER_SELECTOR: crates/ty_python_semantic/resources/primer/good.txt
CLICOLOR_FORCE: "1"
DIFF_FILE: mypy_primer_determinism.diff
run: |
cd ruff
scripts/mypy_primer.sh
- name: Check for non-determinism
run: |
# Remove ANSI color codes for checking
sed -e 's/\x1b\[[0-9;]*m//g' mypy_primer_determinism.diff > mypy_primer_determinism_clean.diff
# Check if there are any differences (non-determinism)
if [ -s mypy_primer_determinism_clean.diff ]; then
echo "ERROR: Non-deterministic output detected!"
echo "The following differences were found when running ty twice on the same commit:"
cat mypy_primer_determinism_clean.diff
exit 1
else
echo "✓ Output is deterministic"
fi

33
Cargo.lock generated
View File

@ -1016,7 +1016,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users", "redox_users",
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@ -1108,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@ -1238,9 +1238,9 @@ dependencies = [
[[package]] [[package]]
name = "get-size-derive2" name = "get-size-derive2"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff47daa61505c85af126e9dd64af6a342a33dc0cccfe1be74ceadc7d352e6efd" checksum = "ab21d7bd2c625f2064f04ce54bcb88bc57c45724cde45cba326d784e22d3f71a"
dependencies = [ dependencies = [
"attribute-derive", "attribute-derive",
"quote", "quote",
@ -1249,14 +1249,15 @@ dependencies = [
[[package]] [[package]]
name = "get-size2" name = "get-size2"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb8710e1f09672102be7ddf39f764d8440ae74a9f4e30aaa4820dcdffa4af" checksum = "879272b0de109e2b67b39fcfe3d25fdbba96ac07e44a254f5a0b4d7ff55340cb"
dependencies = [ dependencies = [
"compact_str", "compact_str",
"get-size-derive2", "get-size-derive2",
"hashbrown 0.16.1", "hashbrown 0.16.1",
"indexmap", "indexmap",
"ordermap",
"smallvec", "smallvec",
] ]
@ -1763,7 +1764,7 @@ dependencies = [
"portable-atomic", "portable-atomic",
"portable-atomic-util", "portable-atomic-util",
"serde_core", "serde_core",
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@ -2233,9 +2234,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "ordermap" name = "ordermap"
version = "0.5.12" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b100f7dd605611822d30e182214d3c02fdefce2d801d23993f6b6ba6ca1392af" checksum = "ed637741ced8fb240855d22a2b4f208dab7a06bcce73380162e5253000c16758"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -3348,6 +3349,7 @@ dependencies = [
"compact_str", "compact_str",
"get-size2", "get-size2",
"insta", "insta",
"itertools 0.14.0",
"memchr", "memchr",
"ruff_annotate_snippets", "ruff_annotate_snippets",
"ruff_python_ast", "ruff_python_ast",
@ -3571,7 +3573,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@ -3589,7 +3591,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]] [[package]]
name = "salsa" name = "salsa"
version = "0.24.0" version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0" source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
dependencies = [ dependencies = [
"boxcar", "boxcar",
"compact_str", "compact_str",
@ -3600,6 +3602,7 @@ dependencies = [
"indexmap", "indexmap",
"intrusive-collections", "intrusive-collections",
"inventory", "inventory",
"ordermap",
"parking_lot", "parking_lot",
"portable-atomic", "portable-atomic",
"rustc-hash", "rustc-hash",
@ -3613,12 +3616,12 @@ dependencies = [
[[package]] [[package]]
name = "salsa-macro-rules" name = "salsa-macro-rules"
version = "0.24.0" version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0" source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
[[package]] [[package]]
name = "salsa-macros" name = "salsa-macros"
version = "0.24.0" version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0" source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3972,7 +3975,7 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@ -5026,7 +5029,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.61.0",
] ]
[[package]] [[package]]

View File

@ -88,7 +88,7 @@ etcetera = { version = "0.11.0" }
fern = { version = "0.7.0" } fern = { version = "0.7.0" }
filetime = { version = "0.2.23" } filetime = { version = "0.2.23" }
getrandom = { version = "0.3.1" } getrandom = { version = "0.3.1" }
get-size2 = { version = "0.7.0", features = [ get-size2 = { version = "0.7.3", features = [
"derive", "derive",
"smallvec", "smallvec",
"hashbrown", "hashbrown",
@ -129,7 +129,7 @@ memchr = { version = "2.7.1" }
mimalloc = { version = "0.1.39" } mimalloc = { version = "0.1.39" }
natord = { version = "1.0.9" } natord = { version = "1.0.9" }
notify = { version = "8.0.0" } notify = { version = "8.0.0" }
ordermap = { version = "0.5.0" } ordermap = { version = "1.0.0" }
path-absolutize = { version = "3.1.1" } path-absolutize = { version = "3.1.1" }
path-slash = { version = "0.2.1" } path-slash = { version = "0.2.1" }
pathdiff = { version = "0.2.1" } pathdiff = { version = "0.2.1" }
@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
rustc-hash = { version = "2.0.0" } rustc-hash = { version = "2.0.0" }
rustc-stable-hash = { version = "0.1.2" } rustc-stable-hash = { version = "0.1.2" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml` # When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0", default-features = false, features = [ salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "55e5e7d32fa3fc189276f35bb04c9438f9aedbd1", default-features = false, features = [
"compact_str", "compact_str",
"macros", "macros",
"salsa_unstable", "salsa_unstable",

View File

@ -194,7 +194,7 @@ static SYMPY: Benchmark = Benchmark::new(
max_dep_date: "2025-06-17", max_dep_date: "2025-06-17",
python_version: PythonVersion::PY312, python_version: PythonVersion::PY312,
}, },
13000, 13030,
); );
static TANJUN: Benchmark = Benchmark::new( static TANJUN: Benchmark = Benchmark::new(

View File

@ -437,6 +437,15 @@ impl<'a> Checker<'a> {
} }
} }
/// Returns the [`Tokens`] for the parsed source file.
///
///
/// Unlike [`Self::tokens`], this method always returns
/// the tokens for the current file, even when within a parsed type annotation.
pub(crate) fn source_tokens(&self) -> &'a Tokens {
self.parsed.tokens()
}
/// The [`Locator`] for the current file, which enables extraction of source code from byte /// The [`Locator`] for the current file, which enables extraction of source code from byte
/// offsets. /// offsets.
pub(crate) const fn locator(&self) -> &'a Locator<'a> { pub(crate) const fn locator(&self) -> &'a Locator<'a> {

View File

@ -3,14 +3,13 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use ruff_python_ast::AnyNodeRef; use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{self, Tokens, parenthesized_range};
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt}; use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
use ruff_python_codegen::Stylist; use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer; use ruff_python_index::Indexer;
use ruff_python_trivia::textwrap::dedent_to; use ruff_python_trivia::textwrap::dedent_to;
use ruff_python_trivia::{ use ruff_python_trivia::{
CommentRanges, PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content, PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content, is_python_whitespace,
is_python_whitespace,
}; };
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines}; use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
@ -209,7 +208,7 @@ pub(crate) fn remove_argument<T: Ranged>(
arguments: &Arguments, arguments: &Arguments,
parentheses: Parentheses, parentheses: Parentheses,
source: &str, source: &str,
comment_ranges: &CommentRanges, tokens: &Tokens,
) -> Result<Edit> { ) -> Result<Edit> {
// Partition into arguments before and after the argument to remove. // Partition into arguments before and after the argument to remove.
let (before, after): (Vec<_>, Vec<_>) = arguments let (before, after): (Vec<_>, Vec<_>) = arguments
@ -224,7 +223,7 @@ pub(crate) fn remove_argument<T: Ranged>(
.context("Unable to find argument")?; .context("Unable to find argument")?;
let parenthesized_range = let parenthesized_range =
parenthesized_range(arg.value().into(), arguments.into(), comment_ranges, source) token::parenthesized_range(arg.value().into(), arguments.into(), tokens)
.unwrap_or(arg.range()); .unwrap_or(arg.range());
if !after.is_empty() { if !after.is_empty() {
@ -270,24 +269,13 @@ pub(crate) fn remove_argument<T: Ranged>(
/// ///
/// The new argument will be inserted before the first existing keyword argument in `arguments`, if /// The new argument will be inserted before the first existing keyword argument in `arguments`, if
/// there are any present. Otherwise, the new argument is added to the end of the argument list. /// there are any present. Otherwise, the new argument is added to the end of the argument list.
pub(crate) fn add_argument( pub(crate) fn add_argument(argument: &str, arguments: &Arguments, tokens: &Tokens) -> Edit {
argument: &str,
arguments: &Arguments,
comment_ranges: &CommentRanges,
source: &str,
) -> Edit {
if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() { if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() {
let keyword = parenthesized_range(value.into(), arguments.into(), comment_ranges, source) let keyword = parenthesized_range(value.into(), arguments.into(), tokens).unwrap_or(*range);
.unwrap_or(*range);
Edit::insertion(format!("{argument}, "), keyword.start()) Edit::insertion(format!("{argument}, "), keyword.start())
} else if let Some(last) = arguments.arguments_source_order().last() { } else if let Some(last) = arguments.arguments_source_order().last() {
// Case 1: existing arguments, so append after the last argument. // Case 1: existing arguments, so append after the last argument.
let last = parenthesized_range( let last = parenthesized_range(last.value().into(), arguments.into(), tokens)
last.value().into(),
arguments.into(),
comment_ranges,
source,
)
.unwrap_or(last.range()); .unwrap_or(last.range());
Edit::insertion(format!(", {argument}"), last.end()) Edit::insertion(format!(", {argument}"), last.end())
} else { } else {

View File

@ -91,8 +91,8 @@ pub(crate) fn fastapi_redundant_response_model(checker: &Checker, function_def:
response_model_arg, response_model_arg,
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.source(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::unsafe_edit) .map(Fix::unsafe_edit)
}); });

View File

@ -74,12 +74,7 @@ pub(crate) fn map_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
checker checker
.report_diagnostic(MapWithoutExplicitStrict, call.range()) .report_diagnostic(MapWithoutExplicitStrict, call.range())
.set_fix(Fix::applicable_edit( .set_fix(Fix::applicable_edit(
add_argument( add_argument("strict=False", &call.arguments, checker.tokens()),
"strict=False",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
),
Applicability::Unsafe, Applicability::Unsafe,
)); ));
} }

View File

@ -3,7 +3,7 @@ use std::fmt::Write;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_docstring_stmt; use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_ast::name::QualifiedName; use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault}; use ruff_python_ast::{self as ast, Expr, ParameterWithDefault};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::function_type::is_stub; use ruff_python_semantic::analyze::function_type::is_stub;
@ -166,12 +166,7 @@ fn move_initialization(
return None; return None;
} }
let range = match parenthesized_range( let range = match parenthesized_range(default.into(), parameter.into(), checker.tokens()) {
default.into(),
parameter.into(),
checker.comment_ranges(),
checker.source(),
) {
Some(range) => range, Some(range) => range,
None => default.range(), None => default.range(),
}; };
@ -194,12 +189,7 @@ fn move_initialization(
"{} = {}", "{} = {}",
parameter.parameter.name(), parameter.parameter.name(),
locator.slice( locator.slice(
parenthesized_range( parenthesized_range(default.into(), parameter.into(), checker.tokens())
default.into(),
parameter.into(),
checker.comment_ranges(),
checker.source()
)
.unwrap_or(default.range()) .unwrap_or(default.range())
) )
); );

View File

@ -92,12 +92,7 @@ pub(crate) fn no_explicit_stacklevel(checker: &Checker, call: &ast::ExprCall) {
} }
let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range()); let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range());
let edit = add_argument( let edit = add_argument("stacklevel=2", &call.arguments, checker.tokens());
"stacklevel=2",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
);
diagnostic.set_fix(Fix::unsafe_edit(edit)); diagnostic.set_fix(Fix::unsafe_edit(edit));
} }

View File

@ -70,12 +70,7 @@ pub(crate) fn zip_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
checker checker
.report_diagnostic(ZipWithoutExplicitStrict, call.range()) .report_diagnostic(ZipWithoutExplicitStrict, call.range())
.set_fix(Fix::applicable_edit( .set_fix(Fix::applicable_edit(
add_argument( add_argument("strict=False", &call.arguments, checker.tokens()),
"strict=False",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
),
Applicability::Unsafe, Applicability::Unsafe,
)); ));
} }

View File

@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::ExprGenerator; use ruff_python_ast::ExprGenerator;
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::token::TokenKind; use ruff_python_ast::token::TokenKind;
use ruff_python_ast::token::parenthesized_range;
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -142,12 +142,8 @@ pub(crate) fn unnecessary_generator_list(checker: &Checker, call: &ast::ExprCall
if *parenthesized { if *parenthesized {
// The generator's range will include the innermost parentheses, but it could be // The generator's range will include the innermost parentheses, but it could be
// surrounded by additional parentheses. // surrounded by additional parentheses.
let range = parenthesized_range( let range =
argument.into(), parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
(&call.arguments).into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(argument.range()); .unwrap_or(argument.range());
// The generator always parenthesizes the expression; trim the parentheses. // The generator always parenthesizes the expression; trim the parentheses.

View File

@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::ExprGenerator; use ruff_python_ast::ExprGenerator;
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::token::TokenKind; use ruff_python_ast::token::TokenKind;
use ruff_python_ast::token::parenthesized_range;
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -147,12 +147,8 @@ pub(crate) fn unnecessary_generator_set(checker: &Checker, call: &ast::ExprCall)
if *parenthesized { if *parenthesized {
// The generator's range will include the innermost parentheses, but it could be // The generator's range will include the innermost parentheses, but it could be
// surrounded by additional parentheses. // surrounded by additional parentheses.
let range = parenthesized_range( let range =
argument.into(), parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
(&call.arguments).into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(argument.range()); .unwrap_or(argument.range());
// The generator always parenthesizes the expression; trim the parentheses. // The generator always parenthesizes the expression; trim the parentheses.

View File

@ -1,7 +1,7 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::token::TokenKind; use ruff_python_ast::token::TokenKind;
use ruff_python_ast::token::parenthesized_range;
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -89,12 +89,8 @@ pub(crate) fn unnecessary_list_comprehension_set(checker: &Checker, call: &ast::
// If the list comprehension is parenthesized, remove the parentheses in addition to // If the list comprehension is parenthesized, remove the parentheses in addition to
// removing the brackets. // removing the brackets.
let replacement_range = parenthesized_range( let replacement_range =
argument.into(), parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
(&call.arguments).into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or_else(|| argument.range()); .unwrap_or_else(|| argument.range());
let span = argument.range().add_start(one).sub_end(one); let span = argument.range().add_start(one).sub_end(one);

View File

@ -1,5 +1,5 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr, Operator}; use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_python_trivia::is_python_whitespace; use ruff_python_trivia::is_python_whitespace;
use ruff_source_file::LineRanges; use ruff_source_file::LineRanges;
@ -88,13 +88,7 @@ pub(crate) fn explicit(checker: &Checker, expr: &Expr) {
checker.report_diagnostic(ExplicitStringConcatenation, expr.range()); checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
let is_parenthesized = |expr: &Expr| { let is_parenthesized = |expr: &Expr| {
parenthesized_range( parenthesized_range(expr.into(), bin_op.into(), checker.tokens()).is_some()
expr.into(),
bin_op.into(),
checker.comment_ranges(),
checker.source(),
)
.is_some()
}; };
// If either `left` or `right` is parenthesized, generating // If either `left` or `right` is parenthesized, generating
// a fix would be too involved. Just report the diagnostic. // a fix would be too involved. Just report the diagnostic.

View File

@ -111,7 +111,6 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
} }
let arguments = &call.arguments; let arguments = &call.arguments;
let source = checker.source();
let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range); let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range);
@ -120,8 +119,8 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
exc_info, exc_info,
arguments, arguments,
Parentheses::Preserve, Parentheses::Preserve,
source, checker.source(),
checker.comment_ranges(), checker.tokens(),
)?; )?;
Ok(Fix::unsafe_edit(edit)) Ok(Fix::unsafe_edit(edit))
}); });

View File

@ -2,7 +2,7 @@ use itertools::Itertools;
use rustc_hash::{FxBuildHasher, FxHashSet}; use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_stdlib::identifiers::is_identifier; use ruff_python_stdlib::identifiers::is_identifier;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -129,8 +129,8 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
keyword, keyword,
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.source(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });
@ -158,8 +158,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
parenthesized_range( parenthesized_range(
value.into(), value.into(),
dict.into(), dict.into(),
checker.comment_ranges(), checker.tokens()
checker.locator().contents(),
) )
.unwrap_or(value.range()) .unwrap_or(value.range())
) )

View File

@ -73,11 +73,11 @@ pub(crate) fn unnecessary_range_start(checker: &Checker, call: &ast::ExprCall) {
let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range()); let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range());
diagnostic.try_set_fix(|| { diagnostic.try_set_fix(|| {
remove_argument( remove_argument(
&start, start,
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.source(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });

View File

@ -160,20 +160,16 @@ fn generate_fix(
) -> anyhow::Result<Fix> { ) -> anyhow::Result<Fix> {
let locator = checker.locator(); let locator = checker.locator();
let source = locator.contents(); let source = locator.contents();
let tokens = checker.tokens();
let deletion = remove_argument( let deletion = remove_argument(
generic_base, generic_base,
arguments, arguments,
Parentheses::Preserve, Parentheses::Preserve,
source, source,
checker.comment_ranges(), tokens,
)?; )?;
let insertion = add_argument( let insertion = add_argument(locator.slice(generic_base), arguments, tokens);
locator.slice(generic_base),
arguments,
checker.comment_ranges(),
source,
);
Ok(Fix::unsafe_edits(deletion, [insertion])) Ok(Fix::unsafe_edits(deletion, [insertion]))
} }

View File

@ -5,7 +5,7 @@ use ruff_python_ast::{
helpers::{pep_604_union, typing_optional}, helpers::{pep_604_union, typing_optional},
name::Name, name::Name,
operator_precedence::OperatorPrecedence, operator_precedence::OperatorPrecedence,
parenthesize::parenthesized_range, token::{Tokens, parenthesized_range},
}; };
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union}; use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -243,12 +243,8 @@ fn create_fix(
let union_expr = pep_604_union(&[new_literal_expr, none_expr]); let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
// Check if we need parentheses to preserve operator precedence // Check if we need parentheses to preserve operator precedence
let content = if needs_parentheses_for_precedence( let content =
semantic, if needs_parentheses_for_precedence(semantic, literal_expr, checker.tokens()) {
literal_expr,
checker.comment_ranges(),
checker.source(),
) {
format!("({})", checker.generator().expr(&union_expr)) format!("({})", checker.generator().expr(&union_expr))
} else { } else {
checker.generator().expr(&union_expr) checker.generator().expr(&union_expr)
@ -278,8 +274,7 @@ enum UnionKind {
fn needs_parentheses_for_precedence( fn needs_parentheses_for_precedence(
semantic: &ruff_python_semantic::SemanticModel, semantic: &ruff_python_semantic::SemanticModel,
literal_expr: &Expr, literal_expr: &Expr,
comment_ranges: &ruff_python_trivia::CommentRanges, tokens: &Tokens,
source: &str,
) -> bool { ) -> bool {
// Get the parent expression to check if we're in a context that needs parentheses // Get the parent expression to check if we're in a context that needs parentheses
let Some(parent_expr) = semantic.current_expression_parent() else { let Some(parent_expr) = semantic.current_expression_parent() else {
@ -287,14 +282,7 @@ fn needs_parentheses_for_precedence(
}; };
// Check if the literal expression is already parenthesized // Check if the literal expression is already parenthesized
if parenthesized_range( if parenthesized_range(literal_expr.into(), parent_expr.into(), tokens).is_some() {
literal_expr.into(),
parent_expr.into(),
comment_ranges,
source,
)
.is_some()
{
return false; // Already parenthesized, don't add more return false; // Already parenthesized, don't add more
} }

View File

@ -10,7 +10,7 @@ use libcst_native::{
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::Truthiness; use ruff_python_ast::helpers::Truthiness;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{ use ruff_python_ast::{
self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp, self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp,
@ -303,8 +303,7 @@ pub(crate) fn unittest_assertion(
parenthesized_range( parenthesized_range(
expr.into(), expr.into(),
checker.semantic().current_statement().into(), checker.semantic().current_statement().into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.unwrap_or(expr.range()), .unwrap_or(expr.range()),
))); )));

View File

@ -768,8 +768,8 @@ fn check_fixture_decorator(checker: &Checker, func_name: &str, decorator: &Decor
keyword, keyword,
arguments, arguments,
edits::Parentheses::Preserve, edits::Parentheses::Preserve,
checker.locator().contents(), checker.source(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::unsafe_edit) .map(Fix::unsafe_edit)
}); });

View File

@ -2,10 +2,9 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{Tokens, parenthesized_range};
use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags}; use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags};
use ruff_python_codegen::Generator; use ruff_python_codegen::Generator;
use ruff_python_trivia::CommentRanges;
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
@ -322,18 +321,8 @@ fn elts_to_csv(elts: &[Expr], generator: Generator, flags: StringLiteralFlags) -
/// ``` /// ```
/// ///
/// This method assumes that the first argument is a string. /// This method assumes that the first argument is a string.
fn get_parametrize_name_range( fn get_parametrize_name_range(call: &ExprCall, expr: &Expr, tokens: &Tokens) -> Option<TextRange> {
call: &ExprCall, parenthesized_range(expr.into(), (&call.arguments).into(), tokens)
expr: &Expr,
comment_ranges: &CommentRanges,
source: &str,
) -> Option<TextRange> {
parenthesized_range(
expr.into(),
(&call.arguments).into(),
comment_ranges,
source,
)
} }
/// PT006 /// PT006
@ -349,12 +338,7 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
if names.len() > 1 { if names.len() > 1 {
match names_type { match names_type {
types::ParametrizeNameType::Tuple => { types::ParametrizeNameType::Tuple => {
let name_range = get_parametrize_name_range( let name_range = get_parametrize_name_range(call, expr, checker.tokens())
call,
expr,
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(expr.range()); .unwrap_or(expr.range());
let mut diagnostic = checker.report_diagnostic( let mut diagnostic = checker.report_diagnostic(
PytestParametrizeNamesWrongType { PytestParametrizeNamesWrongType {
@ -386,12 +370,7 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
))); )));
} }
types::ParametrizeNameType::List => { types::ParametrizeNameType::List => {
let name_range = get_parametrize_name_range( let name_range = get_parametrize_name_range(call, expr, checker.tokens())
call,
expr,
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(expr.range()); .unwrap_or(expr.range());
let mut diagnostic = checker.report_diagnostic( let mut diagnostic = checker.report_diagnostic(
PytestParametrizeNamesWrongType { PytestParametrizeNamesWrongType {

View File

@ -10,7 +10,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::helpers::{Truthiness, contains_effect}; use ruff_python_ast::helpers::{Truthiness, contains_effect};
use ruff_python_ast::name::Name; use ruff_python_ast::name::Name;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_codegen::Generator; use ruff_python_codegen::Generator;
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
@ -800,12 +800,7 @@ fn is_short_circuit(
edit = Some(get_short_circuit_edit( edit = Some(get_short_circuit_edit(
value, value,
TextRange::new( TextRange::new(
parenthesized_range( parenthesized_range(furthest.into(), expr.into(), checker.tokens())
furthest.into(),
expr.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(furthest.range()) .unwrap_or(furthest.range())
.start(), .start(),
expr.end(), expr.end(),
@ -828,12 +823,7 @@ fn is_short_circuit(
edit = Some(get_short_circuit_edit( edit = Some(get_short_circuit_edit(
next_value, next_value,
TextRange::new( TextRange::new(
parenthesized_range( parenthesized_range(furthest.into(), expr.into(), checker.tokens())
furthest.into(),
expr.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(furthest.range()) .unwrap_or(furthest.range())
.start(), .start(),
expr.end(), expr.end(),

View File

@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::{is_const_false, is_const_true}; use ruff_python_ast::helpers::{is_const_false, is_const_true};
use ruff_python_ast::name::Name; use ruff_python_ast::name::Name;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation}; use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
@ -171,12 +171,7 @@ pub(crate) fn if_expr_with_true_false(
checker checker
.locator() .locator()
.slice( .slice(
parenthesized_range( parenthesized_range(test.into(), expr.into(), checker.tokens())
test.into(),
expr.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(test.range()), .unwrap_or(test.range()),
) )
.to_string(), .to_string(),

View File

@ -4,10 +4,10 @@ use anyhow::Result;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableStmt; use ruff_python_ast::comparable::ComparableStmt;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches}; use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches};
use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer}; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::LineRanges; use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -99,7 +99,7 @@ pub(crate) fn if_with_same_arms(checker: &Checker, stmt_if: &ast::StmtIf) {
&current_branch, &current_branch,
following_branch, following_branch,
checker.locator(), checker.locator(),
checker.comment_ranges(), checker.tokens(),
) )
}); });
} }
@ -111,7 +111,7 @@ fn merge_branches(
current_branch: &IfElifBranch, current_branch: &IfElifBranch,
following_branch: &IfElifBranch, following_branch: &IfElifBranch,
locator: &Locator, locator: &Locator,
comment_ranges: &CommentRanges, tokens: &ruff_python_ast::token::Tokens,
) -> Result<Fix> { ) -> Result<Fix> {
// Identify the colon (`:`) at the end of the current branch's test. // Identify the colon (`:`) at the end of the current branch's test.
let Some(current_branch_colon) = let Some(current_branch_colon) =
@ -127,12 +127,9 @@ fn merge_branches(
); );
// If the following test isn't parenthesized, consider parenthesizing it. // If the following test isn't parenthesized, consider parenthesizing it.
let following_branch_test = if let Some(range) = parenthesized_range( let following_branch_test = if let Some(range) =
following_branch.test.into(), parenthesized_range(following_branch.test.into(), stmt_if.into(), tokens)
stmt_if.into(), {
comment_ranges,
locator.contents(),
) {
Cow::Borrowed(locator.slice(range)) Cow::Borrowed(locator.slice(range))
} else if matches!( } else if matches!(
following_branch.test, following_branch.test,
@ -153,16 +150,11 @@ fn merge_branches(
// //
// For example, if the current test is `x if x else y`, we should parenthesize it to // For example, if the current test is `x if x else y`, we should parenthesize it to
// `(x if x else y) or ...`. // `(x if x else y) or ...`.
let parenthesize_edit = if matches!( let parenthesize_edit =
if matches!(
current_branch.test, current_branch.test,
Expr::Lambda(_) | Expr::Named(_) | Expr::If(_) Expr::Lambda(_) | Expr::Named(_) | Expr::If(_)
) && parenthesized_range( ) && parenthesized_range(current_branch.test.into(), stmt_if.into(), tokens).is_none()
current_branch.test.into(),
stmt_if.into(),
comment_ranges,
locator.contents(),
)
.is_none()
{ {
Some(Edit::range_replacement( Some(Edit::range_replacement(
format!("({})", locator.slice(current_branch.test)), format!("({})", locator.slice(current_branch.test)),

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::AnyNodeRef; use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr}; use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
use ruff_python_semantic::analyze::typing; use ruff_python_semantic::analyze::typing;
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
@ -90,20 +90,10 @@ fn key_in_dict(checker: &Checker, left: &Expr, right: &Expr, operator: CmpOp, pa
} }
// Extract the exact range of the left and right expressions. // Extract the exact range of the left and right expressions.
let left_range = parenthesized_range( let left_range =
left.into(), parenthesized_range(left.into(), parent, checker.tokens()).unwrap_or(left.range());
parent, let right_range =
checker.comment_ranges(), parenthesized_range(right.into(), parent, checker.tokens()).unwrap_or(right.range());
checker.locator().contents(),
)
.unwrap_or(left.range());
let right_range = parenthesized_range(
right.into(),
parent,
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(right.range());
let mut diagnostic = checker.report_diagnostic( let mut diagnostic = checker.report_diagnostic(
InDictKeys { InDictKeys {

View File

@ -11,7 +11,7 @@ use crate::registry::Rule;
use crate::rules::flake8_type_checking::helpers::quote_type_expression; use crate::rules::flake8_type_checking::helpers::quote_type_expression;
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation}; use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
use ruff_python_ast::PythonVersion; use ruff_python_ast::PythonVersion;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
/// ## What it does /// ## What it does
/// Checks if [PEP 613] explicit type aliases contain references to /// Checks if [PEP 613] explicit type aliases contain references to
@ -295,21 +295,20 @@ pub(crate) fn quoted_type_alias(
let range = annotation_expr.range(); let range = annotation_expr.range();
let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range); let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range);
let fix_string = annotation_expr.value.to_string(); let fix_string = annotation_expr.value.to_string();
let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r')) let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r'))
&& parenthesized_range( && parenthesized_range(
// Check for parenthesis outside string ("""...""") // Check for parentheses outside the string ("""...""")
annotation_expr.into(), annotation_expr.into(),
checker.semantic().current_statement().into(), checker.semantic().current_statement().into(),
checker.comment_ranges(), checker.source_tokens(),
checker.locator().contents(),
) )
.is_none() .is_none()
&& parenthesized_range( && parenthesized_range(
// Check for parenthesis inside string """(...)""" // Check for parentheses inside the string """(...)"""
expr.into(), expr.into(),
annotation_expr.into(), annotation_expr.into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.is_none() .is_none()
{ {

View File

@ -1,10 +1,9 @@
use std::ops::Range; use std::ops::Range;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator}; use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -89,11 +88,7 @@ pub(crate) fn path_constructor_current_directory(
let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range()); let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range());
match parent_and_next_path_fragment_range( match parent_and_next_path_fragment_range(checker.semantic(), checker.tokens()) {
checker.semantic(),
checker.comment_ranges(),
checker.source(),
) {
Some((parent_range, next_fragment_range)) => { Some((parent_range, next_fragment_range)) => {
let next_fragment_expr = checker.locator().slice(next_fragment_range); let next_fragment_expr = checker.locator().slice(next_fragment_range);
let call_expr = checker.locator().slice(call.range()); let call_expr = checker.locator().slice(call.range());
@ -116,7 +111,7 @@ pub(crate) fn path_constructor_current_directory(
arguments, arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.source(), checker.source(),
checker.comment_ranges(), checker.tokens(),
)?; )?;
Ok(Fix::applicable_edit(edit, applicability(call.range()))) Ok(Fix::applicable_edit(edit, applicability(call.range())))
}), }),
@ -125,8 +120,7 @@ pub(crate) fn path_constructor_current_directory(
fn parent_and_next_path_fragment_range( fn parent_and_next_path_fragment_range(
semantic: &SemanticModel, semantic: &SemanticModel,
comment_ranges: &CommentRanges, tokens: &ruff_python_ast::token::Tokens,
source: &str,
) -> Option<(TextRange, TextRange)> { ) -> Option<(TextRange, TextRange)> {
let parent = semantic.current_expression_parent()?; let parent = semantic.current_expression_parent()?;
@ -142,6 +136,6 @@ fn parent_and_next_path_fragment_range(
Some(( Some((
parent.range(), parent.range(),
parenthesized_range(right.into(), parent.into(), comment_ranges, source).unwrap_or(range), parenthesized_range(right.into(), parent.into(), tokens).unwrap_or(range),
)) ))
} }

View File

@ -1,8 +1,7 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_const_true; use ruff_python_ast::helpers::is_const_true;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{Tokens, parenthesized_range};
use ruff_python_ast::{self as ast, Keyword, Stmt}; use ruff_python_ast::{self as ast, Keyword, Stmt};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::Locator; use crate::Locator;
@ -91,7 +90,7 @@ pub(crate) fn inplace_argument(checker: &Checker, call: &ast::ExprCall) {
call, call,
keyword, keyword,
statement, statement,
checker.comment_ranges(), checker.tokens(),
checker.locator(), checker.locator(),
) { ) {
diagnostic.set_fix(fix); diagnostic.set_fix(fix);
@ -111,19 +110,14 @@ fn convert_inplace_argument_to_assignment(
call: &ast::ExprCall, call: &ast::ExprCall,
keyword: &Keyword, keyword: &Keyword,
statement: &Stmt, statement: &Stmt,
comment_ranges: &CommentRanges, tokens: &Tokens,
locator: &Locator, locator: &Locator,
) -> Option<Fix> { ) -> Option<Fix> {
// Add the assignment. // Add the assignment.
let attr = call.func.as_attribute_expr()?; let attr = call.func.as_attribute_expr()?;
let insert_assignment = Edit::insertion( let insert_assignment = Edit::insertion(
format!("{name} = ", name = locator.slice(attr.value.range())), format!("{name} = ", name = locator.slice(attr.value.range())),
parenthesized_range( parenthesized_range(call.into(), statement.into(), tokens)
call.into(),
statement.into(),
comment_ranges,
locator.contents(),
)
.unwrap_or(call.range()) .unwrap_or(call.range())
.start(), .start(),
); );
@ -134,7 +128,7 @@ fn convert_inplace_argument_to_assignment(
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
locator.contents(), locator.contents(),
comment_ranges, tokens,
) )
.ok()?; .ok()?;

View File

@ -1,5 +1,5 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{ use ruff_python_ast::{
self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter, self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter,
ParameterWithDefault, Parameters, Stmt, ParameterWithDefault, Parameters, Stmt,
@ -265,25 +265,15 @@ fn replace_trailing_ellipsis_with_original_expr(
stmt: &Stmt, stmt: &Stmt,
checker: &Checker, checker: &Checker,
) -> String { ) -> String {
let original_expr_range = parenthesized_range( let original_expr_range =
(&lambda.body).into(), parenthesized_range((&lambda.body).into(), lambda.into(), checker.tokens())
lambda.into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(lambda.body.range()); .unwrap_or(lambda.body.range());
// This prevents the autofix of introducing a syntax error if the lambda's body is an // This prevents the autofix of introducing a syntax error if the lambda's body is an
// expression spanned across multiple lines. To avoid the syntax error we preserve // expression spanned across multiple lines. To avoid the syntax error we preserve
// the parenthesis around the body. // the parenthesis around the body.
let original_expr_in_source = if parenthesized_range( let original_expr_in_source =
lambda.into(), if parenthesized_range(lambda.into(), stmt.into(), checker.tokens()).is_some() {
stmt.into(),
checker.comment_ranges(),
checker.source(),
)
.is_some()
{
format!("({})", checker.locator().slice(original_expr_range)) format!("({})", checker.locator().slice(original_expr_range))
} else { } else {
checker.locator().slice(original_expr_range).to_string() checker.locator().slice(original_expr_range).to_string()

View File

@ -1,4 +1,4 @@
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{Tokens, parenthesized_range};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
@ -179,14 +179,13 @@ fn is_redundant_boolean_comparison(op: CmpOp, comparator: &Expr) -> Option<bool>
fn generate_redundant_comparison( fn generate_redundant_comparison(
compare: &ast::ExprCompare, compare: &ast::ExprCompare,
comment_ranges: &ruff_python_trivia::CommentRanges, tokens: &Tokens,
source: &str, source: &str,
comparator: &Expr, comparator: &Expr,
kind: bool, kind: bool,
needs_wrap: bool, needs_wrap: bool,
) -> String { ) -> String {
let comparator_range = let comparator_range = parenthesized_range(comparator.into(), compare.into(), tokens)
parenthesized_range(comparator.into(), compare.into(), comment_ranges, source)
.unwrap_or(comparator.range()); .unwrap_or(comparator.range());
let comparator_str = &source[comparator_range]; let comparator_str = &source[comparator_range];
@ -379,7 +378,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
.copied() .copied()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let comment_ranges = checker.comment_ranges(); let tokens = checker.tokens();
let source = checker.source(); let source = checker.source();
let content = match (&*compare.ops, &*compare.comparators) { let content = match (&*compare.ops, &*compare.comparators) {
@ -387,18 +386,13 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) { if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) {
let needs_wrap = compare.left.range().start() != compare.range().start(); let needs_wrap = compare.left.range().start() != compare.range().start();
generate_redundant_comparison( generate_redundant_comparison(
compare, compare, tokens, source, comparator, kind, needs_wrap,
comment_ranges,
source,
comparator,
kind,
needs_wrap,
) )
} else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) { } else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) {
let needs_wrap = comparator.range().end() != compare.range().end(); let needs_wrap = comparator.range().end() != compare.range().end();
generate_redundant_comparison( generate_redundant_comparison(
compare, compare,
comment_ranges, tokens,
source, source,
&compare.left, &compare.left,
kind, kind,
@ -410,7 +404,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
&ops, &ops,
&compare.comparators, &compare.comparators,
compare.into(), compare.into(),
comment_ranges, tokens,
source, source,
) )
} }
@ -420,7 +414,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
&ops, &ops,
&compare.comparators, &compare.comparators,
compare.into(), compare.into(),
comment_ranges, tokens,
source, source,
), ),
}; };

View File

@ -107,7 +107,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
&[CmpOp::NotIn], &[CmpOp::NotIn],
comparators, comparators,
unary_op.into(), unary_op.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(), checker.source(),
), ),
unary_op.range(), unary_op.range(),
@ -127,7 +127,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
&[CmpOp::IsNot], &[CmpOp::IsNot],
comparators, comparators,
unary_op.into(), unary_op.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(), checker.source(),
), ),
unary_op.range(), unary_op.range(),

View File

@ -3,7 +3,7 @@ use std::collections::hash_map::Entry;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::{ComparableExpr, HashableExpr}; use ruff_python_ast::comparable::{ComparableExpr, HashableExpr};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -193,16 +193,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
parenthesized_range( parenthesized_range(
dict.value(i - 1).into(), dict.value(i - 1).into(),
dict.into(), dict.into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.unwrap_or_else(|| dict.value(i - 1).range()) .unwrap_or_else(|| dict.value(i - 1).range())
.end(), .end(),
parenthesized_range( parenthesized_range(
dict.value(i).into(), dict.value(i).into(),
dict.into(), dict.into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.unwrap_or_else(|| dict.value(i).range()) .unwrap_or_else(|| dict.value(i).range())
.end(), .end(),
@ -224,16 +222,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
parenthesized_range( parenthesized_range(
dict.value(i - 1).into(), dict.value(i - 1).into(),
dict.into(), dict.into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.unwrap_or_else(|| dict.value(i - 1).range()) .unwrap_or_else(|| dict.value(i - 1).range())
.end(), .end(),
parenthesized_range( parenthesized_range(
dict.value(i).into(), dict.value(i).into(),
dict.into(), dict.into(),
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
) )
.unwrap_or_else(|| dict.value(i).range()) .unwrap_or_else(|| dict.value(i).range())
.end(), .end(),

View File

@ -2,7 +2,7 @@ use itertools::Itertools;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::contains_effect; use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::token::{TokenKind, Tokens}; use ruff_python_ast::token::{TokenKind, Tokens};
use ruff_python_ast::{self as ast, Stmt}; use ruff_python_ast::{self as ast, Stmt};
use ruff_python_semantic::Binding; use ruff_python_semantic::Binding;
@ -172,12 +172,8 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
{ {
// If the expression is complex (`x = foo()`), remove the assignment, // If the expression is complex (`x = foo()`), remove the assignment,
// but preserve the right-hand side. // but preserve the right-hand side.
let start = parenthesized_range( let start =
target.into(), parenthesized_range(target.into(), statement.into(), checker.tokens())
statement.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(target.range()) .unwrap_or(target.range())
.start(); .start();
let end = match_token_after(checker.tokens(), target.end(), |token| { let end = match_token_after(checker.tokens(), target.end(), |token| {

View File

@ -2,7 +2,7 @@ use itertools::Itertools;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{ use ruff_python_ast::{
BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare, BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare,
parenthesize::{parentheses_iterator, parenthesized_range}, token::{parentheses_iterator, parenthesized_range},
}; };
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -62,7 +62,7 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
} }
let locator = checker.locator(); let locator = checker.locator();
let comment_ranges = checker.comment_ranges(); let tokens = checker.tokens();
// retrieve all compare expressions from boolean expression // retrieve all compare expressions from boolean expression
let compare_expressions = expr_bool_op let compare_expressions = expr_bool_op
@ -89,39 +89,21 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
continue; continue;
} }
let left_paren_count = parentheses_iterator( let left_paren_count =
left_compare.into(), parentheses_iterator(left_compare.into(), Some(expr_bool_op.into()), tokens).count();
Some(expr_bool_op.into()),
comment_ranges,
locator.contents(),
)
.count();
let right_paren_count = parentheses_iterator( let right_paren_count =
right_compare.into(), parentheses_iterator(right_compare.into(), Some(expr_bool_op.into()), tokens).count();
Some(expr_bool_op.into()),
comment_ranges,
locator.contents(),
)
.count();
// Create the edit that removes the comparison operator // Create the edit that removes the comparison operator
// In `a<(b) and ((b))<c`, we need to handle the // In `a<(b) and ((b))<c`, we need to handle the
// parentheses when specifying the fix range. // parentheses when specifying the fix range.
let left_compare_right_range = parenthesized_range( let left_compare_right_range =
left_compare_right.into(), parenthesized_range(left_compare_right.into(), left_compare.into(), tokens)
left_compare.into(),
comment_ranges,
locator.contents(),
)
.unwrap_or(left_compare_right.range()); .unwrap_or(left_compare_right.range());
let right_compare_left_range = parenthesized_range( let right_compare_left_range =
right_compare_left.into(), parenthesized_range(right_compare_left.into(), right_compare.into(), tokens)
right_compare.into(),
comment_ranges,
locator.contents(),
)
.unwrap_or(right_compare_left.range()); .unwrap_or(right_compare_left.range());
let edit = Edit::range_replacement( let edit = Edit::range_replacement(
locator.slice(left_compare_right_range).to_string(), locator.slice(left_compare_right_range).to_string(),

View File

@ -99,7 +99,7 @@ pub(crate) fn duplicate_bases(checker: &Checker, name: &str, arguments: Option<&
arguments, arguments,
Parentheses::Remove, Parentheses::Remove,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(|edit| { .map(|edit| {
Fix::applicable_edit( Fix::applicable_edit(

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, CmpOp, Stmt}; use ruff_python_ast::{self as ast, CmpOp, Stmt};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -166,12 +166,7 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
let replacement = format!( let replacement = format!(
"{} = {min_max}({}, {})", "{} = {min_max}({}, {})",
checker.locator().slice( checker.locator().slice(
parenthesized_range( parenthesized_range(body_target.into(), body.into(), checker.tokens())
body_target.into(),
body.into(),
checker.comment_ranges(),
checker.locator().contents()
)
.unwrap_or(body_target.range()) .unwrap_or(body_target.range())
), ),
checker.locator().slice(arg1), checker.locator().slice(arg1),

View File

@ -174,12 +174,8 @@ pub(crate) fn missing_maxsplit_arg(checker: &Checker, value: &Expr, slice: &Expr
SliceBoundary::Last => "rsplit", SliceBoundary::Last => "rsplit",
}; };
let maxsplit_argument_edit = fix::edits::add_argument( let maxsplit_argument_edit =
"maxsplit=1", fix::edits::add_argument("maxsplit=1", arguments, checker.tokens());
arguments,
checker.comment_ranges(),
checker.locator().contents(),
);
// Only change `actual_split_type` if it doesn't match `suggested_split_type` // Only change `actual_split_type` if it doesn't match `suggested_split_type`
let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type { let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type {

View File

@ -2,7 +2,7 @@ use ast::Expr;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{ExprBinOp, ExprRef, Operator}; use ruff_python_ast::{ExprBinOp, ExprRef, Operator};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -150,12 +150,10 @@ fn augmented_assignment(
let right_operand_ref = ExprRef::from(right_operand); let right_operand_ref = ExprRef::from(right_operand);
let parent = original_expr.into(); let parent = original_expr.into();
let comment_ranges = checker.comment_ranges(); let tokens = checker.tokens();
let source = checker.source();
let right_operand_range = let right_operand_range =
parenthesized_range(right_operand_ref, parent, comment_ranges, source) parenthesized_range(right_operand_ref, parent, tokens).unwrap_or(right_operand.range());
.unwrap_or(right_operand.range());
let right_operand_expr = locator.slice(right_operand_range); let right_operand_expr = locator.slice(right_operand_range);
let target_expr = locator.slice(target); let target_expr = locator.slice(target);

View File

@ -75,12 +75,7 @@ pub(crate) fn subprocess_run_without_check(checker: &Checker, call: &ast::ExprCa
let mut diagnostic = let mut diagnostic =
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range()); checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
diagnostic.set_fix(Fix::applicable_edit( diagnostic.set_fix(Fix::applicable_edit(
add_argument( add_argument("check=False", &call.arguments, checker.tokens()),
"check=False",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
),
// If the function call contains `**kwargs`, mark the fix as unsafe. // If the function call contains `**kwargs`, mark the fix as unsafe.
if call if call
.arguments .arguments

View File

@ -1,8 +1,7 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName; use ruff_python_ast::{self as ast, Expr, name::QualifiedName};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::typing; use ruff_python_semantic::analyze::typing;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -193,8 +192,7 @@ fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix {
})) }))
), ),
&call.arguments, &call.arguments,
checker.comment_ranges(), checker.tokens(),
checker.locator().contents(),
)) ))
} }

View File

@ -204,7 +204,7 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
arguments, arguments,
Parentheses::Remove, Parentheses::Remove,
checker.source(), checker.source(),
checker.comment_ranges(), checker.tokens(),
)?; )?;
Ok(Fix::unsafe_edits( Ok(Fix::unsafe_edits(
Edit::insertion(type_params.to_string(), name.end()), Edit::insertion(type_params.to_string(), name.end()),

View File

@ -2,7 +2,7 @@ use itertools::Itertools;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::Name; use ruff_python_ast::name::Name;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef}; use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -261,11 +261,11 @@ fn create_diagnostic(
type_alias_kind: TypeAliasKind, type_alias_kind: TypeAliasKind,
) { ) {
let source = checker.source(); let source = checker.source();
let tokens = checker.tokens();
let comment_ranges = checker.comment_ranges(); let comment_ranges = checker.comment_ranges();
let range_with_parentheses = let range_with_parentheses =
parenthesized_range(value.into(), stmt.into(), comment_ranges, source) parenthesized_range(value.into(), stmt.into(), tokens).unwrap_or(value.range());
.unwrap_or(value.range());
let content = format!( let content = format!(
"type {name}{type_params} = {value}", "type {name}{type_params} = {value}",

View File

@ -1,9 +1,8 @@
use anyhow::Result; use anyhow::Result;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Keyword}; use ruff_python_ast::{self as ast, Keyword, token::Tokens};
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
use ruff_python_trivia::CommentRanges;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -104,7 +103,7 @@ pub(crate) fn replace_stdout_stderr(checker: &Checker, call: &ast::ExprCall) {
stderr, stderr,
call, call,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
}); });
} }
@ -117,7 +116,7 @@ fn generate_fix(
stderr: &Keyword, stderr: &Keyword,
call: &ast::ExprCall, call: &ast::ExprCall,
source: &str, source: &str,
comment_ranges: &CommentRanges, tokens: &Tokens,
) -> Result<Fix> { ) -> Result<Fix> {
let (first, second) = if stdout.start() < stderr.start() { let (first, second) = if stdout.start() < stderr.start() {
(stdout, stderr) (stdout, stderr)
@ -132,7 +131,7 @@ fn generate_fix(
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
source, source,
comment_ranges, tokens,
)?], )?],
)) ))
} }

View File

@ -78,7 +78,7 @@ pub(crate) fn replace_universal_newlines(checker: &Checker, call: &ast::ExprCall
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });

View File

@ -188,7 +188,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });
@ -206,7 +206,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });
@ -231,7 +231,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });
@ -249,7 +249,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(Fix::safe_edit) .map(Fix::safe_edit)
}); });

View File

@ -70,7 +70,7 @@ pub(crate) fn useless_class_metaclass_type(checker: &Checker, class_def: &StmtCl
arguments, arguments,
Parentheses::Remove, Parentheses::Remove,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
)?; )?;
let range = edit.range(); let range = edit.range();

View File

@ -73,7 +73,7 @@ pub(crate) fn useless_object_inheritance(checker: &Checker, class_def: &ast::Stm
arguments, arguments,
Parentheses::Remove, Parentheses::Remove,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
)?; )?;
let range = edit.range(); let range = edit.range();

View File

@ -1,5 +1,5 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr, Stmt}; use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -139,12 +139,7 @@ pub(crate) fn yield_in_for_loop(checker: &Checker, stmt_for: &ast::StmtFor) {
let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range()); let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range());
let contents = checker.locator().slice( let contents = checker.locator().slice(
parenthesized_range( parenthesized_range(iter.as_ref().into(), stmt_for.into(), checker.tokens())
iter.as_ref().into(),
stmt_for.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(iter.range()), .unwrap_or(iter.range()),
); );
let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) { let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) {

View File

@ -1,7 +1,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use ruff_python_ast::PythonVersion; use ruff_python_ast::PythonVersion;
use ruff_python_ast::{self as ast, Expr, name::Name, parenthesize::parenthesized_range}; use ruff_python_ast::{self as ast, Expr, name::Name, token::parenthesized_range};
use ruff_python_codegen::Generator; use ruff_python_codegen::Generator;
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel}; use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
@ -330,12 +330,8 @@ pub(super) fn parenthesize_loop_iter_if_necessary<'a>(
let locator = checker.locator(); let locator = checker.locator();
let iter = for_stmt.iter.as_ref(); let iter = for_stmt.iter.as_ref();
let original_parenthesized_range = parenthesized_range( let original_parenthesized_range =
iter.into(), parenthesized_range(iter.into(), for_stmt.into(), checker.tokens());
for_stmt.into(),
checker.comment_ranges(),
checker.source(),
);
if let Some(range) = original_parenthesized_range { if let Some(range) = original_parenthesized_range {
return Cow::Borrowed(locator.slice(range)); return Cow::Borrowed(locator.slice(range));

View File

@ -1,5 +1,5 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{ use ruff_python_ast::{
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp, Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
Number, Operator, PythonVersion, UnaryOp, Number, Operator, PythonVersion, UnaryOp,
@ -112,8 +112,7 @@ pub(crate) fn fromisoformat_replace_z(checker: &Checker, call: &ExprCall) {
let value_full_range = parenthesized_range( let value_full_range = parenthesized_range(
replace_time_zone.date.into(), replace_time_zone.date.into(),
replace_time_zone.parent.into(), replace_time_zone.parent.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(),
) )
.unwrap_or(replace_time_zone.date.range()); .unwrap_or(replace_time_zone.date.range());

View File

@ -5,8 +5,7 @@ use ruff_python_ast as ast;
use ruff_python_ast::Expr; use ruff_python_ast::Expr;
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::helpers::contains_effect; use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{Tokens, parenthesized_range};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::Locator; use crate::Locator;
@ -76,8 +75,8 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
Edit::range_replacement( Edit::range_replacement(
format!( format!(
"{} or {}", "{} or {}",
parenthesize_test(test, if_expr, checker.comment_ranges(), checker.locator()), parenthesize_test(test, if_expr, checker.tokens(), checker.locator()),
parenthesize_test(orelse, if_expr, checker.comment_ranges(), checker.locator()), parenthesize_test(orelse, if_expr, checker.tokens(), checker.locator()),
), ),
if_expr.range(), if_expr.range(),
), ),
@ -99,15 +98,10 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
fn parenthesize_test<'a>( fn parenthesize_test<'a>(
expr: &Expr, expr: &Expr,
if_expr: &ast::ExprIf, if_expr: &ast::ExprIf,
comment_ranges: &CommentRanges, tokens: &Tokens,
locator: &Locator<'a>, locator: &Locator<'a>,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
if let Some(range) = parenthesized_range( if let Some(range) = parenthesized_range(expr.into(), if_expr.into(), tokens) {
expr.into(),
if_expr.into(),
comment_ranges,
locator.contents(),
) {
Cow::Borrowed(locator.slice(range)) Cow::Borrowed(locator.slice(range))
} else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) { } else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) {
Cow::Owned(format!("({})", locator.slice(expr.range()))) Cow::Owned(format!("({})", locator.slice(expr.range())))

View File

@ -1,6 +1,6 @@
use ruff_diagnostics::Applicability; use ruff_diagnostics::Applicability;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{Comprehension, Expr, StmtFor}; use ruff_python_ast::{Comprehension, Expr, StmtFor};
use ruff_python_semantic::analyze::typing; use ruff_python_semantic::analyze::typing;
use ruff_python_semantic::analyze::typing::is_io_base_expr; use ruff_python_semantic::analyze::typing::is_io_base_expr;
@ -104,8 +104,7 @@ fn readlines_in_iter(checker: &Checker, iter_expr: &Expr) {
let deletion_range = if let Some(parenthesized_range) = parenthesized_range( let deletion_range = if let Some(parenthesized_range) = parenthesized_range(
expr_attr.value.as_ref().into(), expr_attr.value.as_ref().into(),
expr_attr.into(), expr_attr.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(),
) { ) {
expr_call.range().add_start(parenthesized_range.len()) expr_call.range().add_start(parenthesized_range.len())
} else { } else {

View File

@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use ruff_diagnostics::Applicability; use ruff_diagnostics::Applicability;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr, Number}; use ruff_python_ast::{self as ast, Expr, Number};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -152,13 +152,8 @@ fn generate_fix(checker: &Checker, call: &ast::ExprCall, base: Base, arg: &Expr)
checker.semantic(), checker.semantic(),
)?; )?;
let arg_range = parenthesized_range( let arg_range =
arg.into(), parenthesized_range(arg.into(), call.into(), checker.tokens()).unwrap_or(arg.range());
call.into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(arg.range());
let arg_str = checker.locator().slice(arg_range); let arg_str = checker.locator().slice(arg_range);
Ok(Fix::applicable_edits( Ok(Fix::applicable_edits(

View File

@ -95,7 +95,7 @@ pub(crate) fn single_item_membership_test(
&[membership_test.replacement_op()], &[membership_test.replacement_op()],
std::slice::from_ref(item), std::slice::from_ref(item),
expr.into(), expr.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(), checker.source(),
), ),
expr.range(), expr.range(),

View File

@ -163,7 +163,7 @@ fn convert_type_vars(
class_arguments, class_arguments,
Parentheses::Remove, Parentheses::Remove,
source, source,
checker.comment_ranges(), checker.tokens(),
)?; )?;
let replace_type_params = let replace_type_params =
Edit::range_replacement(new_type_params.to_string(), type_params.range); Edit::range_replacement(new_type_params.to_string(), type_params.range);

View File

@ -3,8 +3,8 @@ use anyhow::Result;
use ast::Keyword; use ast::Keyword;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_constant; use ruff_python_ast::helpers::is_constant;
use ruff_python_ast::token::Tokens;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::Locator; use crate::Locator;
@ -108,9 +108,8 @@ pub(crate) fn default_factory_kwarg(checker: &Checker, call: &ast::ExprCall) {
}, },
call.range(), call.range(),
); );
diagnostic.try_set_fix(|| { diagnostic
convert_to_positional(call, keyword, checker.locator(), checker.comment_ranges()) .try_set_fix(|| convert_to_positional(call, keyword, checker.locator(), checker.tokens()));
});
} }
/// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`). /// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`).
@ -136,7 +135,7 @@ fn convert_to_positional(
call: &ast::ExprCall, call: &ast::ExprCall,
default_factory: &Keyword, default_factory: &Keyword,
locator: &Locator, locator: &Locator,
comment_ranges: &CommentRanges, tokens: &Tokens,
) -> Result<Fix> { ) -> Result<Fix> {
if call.arguments.len() == 1 { if call.arguments.len() == 1 {
// Ex) `defaultdict(default_factory=list)` // Ex) `defaultdict(default_factory=list)`
@ -153,7 +152,7 @@ fn convert_to_positional(
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
locator.contents(), locator.contents(),
comment_ranges, tokens,
)?; )?;
// Second, insert the value as the first positional argument. // Second, insert the value as the first positional argument.

View File

@ -128,7 +128,7 @@ pub(crate) fn falsy_dict_get_fallback(checker: &Checker, expr: &Expr) {
&call.arguments, &call.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.locator().contents(), checker.locator().contents(),
checker.comment_ranges(), checker.tokens(),
) )
.map(|edit| Fix::applicable_edit(edit, applicability)) .map(|edit| Fix::applicable_edit(edit, applicability))
}); });

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -77,14 +77,7 @@ pub(crate) fn parenthesize_chained_logical_operators(checker: &Checker, expr: &a
) => { ) => {
let locator = checker.locator(); let locator = checker.locator();
let source_range = bool_op.range(); let source_range = bool_op.range();
if parenthesized_range( if parenthesized_range(bool_op.into(), expr.into(), checker.tokens()).is_none() {
bool_op.into(),
expr.into(),
checker.comment_ranges(),
locator.contents(),
)
.is_none()
{
let new_source = format!("({})", locator.slice(source_range)); let new_source = format!("({})", locator.slice(source_range));
let edit = Edit::range_replacement(new_source, source_range); let edit = Edit::range_replacement(new_source, source_range);
checker checker

View File

@ -2,7 +2,7 @@ use anyhow::Context;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_semantic::{Scope, ScopeKind}; use ruff_python_semantic::{Scope, ScopeKind};
use ruff_python_trivia::{indentation_at_offset, textwrap}; use ruff_python_trivia::{indentation_at_offset, textwrap};
use ruff_source_file::LineRanges; use ruff_source_file::LineRanges;
@ -159,8 +159,7 @@ fn use_initvar(
let default_loc = parenthesized_range( let default_loc = parenthesized_range(
default.into(), default.into(),
parameter_with_default.into(), parameter_with_default.into(),
checker.comment_ranges(), checker.tokens(),
checker.source(),
) )
.unwrap_or(default.range()); .unwrap_or(default.range());

View File

@ -2,7 +2,7 @@ use anyhow::Result;
use itertools::Itertools; use itertools::Itertools;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Arguments, Expr}; use ruff_python_ast::{self as ast, Arguments, Expr};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -116,12 +116,7 @@ fn convert_to_reduce(iterable: &Expr, call: &ast::ExprCall, checker: &Checker) -
)?; )?;
let iterable = checker.locator().slice( let iterable = checker.locator().slice(
parenthesized_range( parenthesized_range(iterable.into(), (&call.arguments).into(), checker.tokens())
iterable.into(),
(&call.arguments).into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(iterable.range()), .unwrap_or(iterable.range()),
); );

View File

@ -1,7 +1,7 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::PythonVersion; use ruff_python_ast::PythonVersion;
use ruff_python_ast::token::TokenKind; use ruff_python_ast::token::TokenKind;
use ruff_python_ast::{Expr, ExprCall, parenthesize::parenthesized_range}; use ruff_python_ast::{Expr, ExprCall, token::parenthesized_range};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -124,13 +124,8 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
let mut remove_zip = vec![]; let mut remove_zip = vec![];
let full_zip_range = parenthesized_range( let full_zip_range =
zip.into(), parenthesized_range(zip.into(), starmap.into(), checker.tokens()).unwrap_or(zip.range());
starmap.into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(zip.range());
// Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple. // Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple.
remove_zip.push(Edit::range_deletion(TextRange::new( remove_zip.push(Edit::range_deletion(TextRange::new(
@ -138,12 +133,7 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
zip.start(), zip.start(),
))); )));
let full_zip_func_range = parenthesized_range( let full_zip_func_range = parenthesized_range((&zip.func).into(), zip.into(), checker.tokens())
(&zip.func).into(),
zip.into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(zip.func.range()); .unwrap_or(zip.func.range());
// Delete the `zip` callee // Delete the `zip` callee

View File

@ -1,5 +1,5 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::{Tokens, parenthesized_range};
use ruff_python_ast::{Arguments, Expr, ExprCall}; use ruff_python_ast::{Arguments, Expr, ExprCall};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType}; use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
@ -86,6 +86,7 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
applicability, applicability,
checker.semantic(), checker.semantic(),
checker.locator(), checker.locator(),
checker.tokens(),
checker.comment_ranges(), checker.comment_ranges(),
checker.source(), checker.source(),
); );
@ -95,27 +96,26 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
} }
/// Creates a fix that replaces `int(expression)` with `expression`. /// Creates a fix that replaces `int(expression)` with `expression`.
#[allow(clippy::too_many_arguments)]
fn unwrap_int_expression( fn unwrap_int_expression(
call: &ExprCall, call: &ExprCall,
argument: &Expr, argument: &Expr,
applicability: Applicability, applicability: Applicability,
semantic: &SemanticModel, semantic: &SemanticModel,
locator: &Locator, locator: &Locator,
tokens: &Tokens,
comment_ranges: &CommentRanges, comment_ranges: &CommentRanges,
source: &str, source: &str,
) -> Fix { ) -> Fix {
let content = if let Some(range) = parenthesized_range( let content = if let Some(range) =
argument.into(), parenthesized_range(argument.into(), (&call.arguments).into(), tokens)
(&call.arguments).into(), {
comment_ranges,
source,
) {
locator.slice(range).to_string() locator.slice(range).to_string()
} else { } else {
let parenthesize = semantic.current_expression_parent().is_some() let parenthesize = semantic.current_expression_parent().is_some()
|| argument.is_named_expr() || argument.is_named_expr()
|| locator.count_lines(argument.range()) > 0; || locator.count_lines(argument.range()) > 0;
if parenthesize && !has_own_parentheses(argument, comment_ranges, source) { if parenthesize && !has_own_parentheses(argument, tokens, source) {
format!("({})", locator.slice(argument.range())) format!("({})", locator.slice(argument.range()))
} else { } else {
locator.slice(argument.range()).to_string() locator.slice(argument.range()).to_string()
@ -255,7 +255,7 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio
} }
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`). /// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str) -> bool { fn has_own_parentheses(expr: &Expr, tokens: &Tokens, source: &str) -> bool {
match expr { match expr {
Expr::ListComp(_) Expr::ListComp(_)
| Expr::SetComp(_) | Expr::SetComp(_)
@ -276,12 +276,8 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
// f // f
// (10) // (10)
// ``` // ```
let func_end = parenthesized_range( let func_end =
call_expr.func.as_ref().into(), parenthesized_range(call_expr.func.as_ref().into(), call_expr.into(), tokens)
call_expr.into(),
comment_ranges,
source,
)
.unwrap_or(call_expr.func.range()) .unwrap_or(call_expr.func.range())
.end(); .end();
lines_after_ignoring_trivia(func_end, source) == 0 lines_after_ignoring_trivia(func_end, source) == 0
@ -291,8 +287,7 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
let subscript_end = parenthesized_range( let subscript_end = parenthesized_range(
subscript_expr.value.as_ref().into(), subscript_expr.value.as_ref().into(),
subscript_expr.into(), subscript_expr.into(),
comment_ranges, tokens,
source,
) )
.unwrap_or(subscript_expr.value.range()) .unwrap_or(subscript_expr.value.range())
.end(); .end();

View File

@ -3,7 +3,7 @@ use ruff_python_ast::{self as ast, BoolOp, CmpOp, Expr};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::contains_effect; use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -108,21 +108,11 @@ pub(crate) fn unnecessary_key_check(checker: &Checker, expr: &Expr) {
format!( format!(
"{}.get({})", "{}.get({})",
checker.locator().slice( checker.locator().slice(
parenthesized_range( parenthesized_range(obj_right.into(), right.into(), checker.tokens(),)
obj_right.into(),
right.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(obj_right.range()) .unwrap_or(obj_right.range())
), ),
checker.locator().slice( checker.locator().slice(
parenthesized_range( parenthesized_range(key_right.into(), right.into(), checker.tokens(),)
key_right.into(),
right.into(),
checker.comment_ranges(),
checker.locator().contents(),
)
.unwrap_or(key_right.range()) .unwrap_or(key_right.range())
), ),
), ),

View File

@ -2,7 +2,7 @@ use ruff_diagnostics::{Applicability, Edit};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_empty_f_string; use ruff_python_ast::helpers::is_empty_f_string;
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -140,31 +140,19 @@ fn fix_unnecessary_literal_in_deque(
// call. otherwise, we only delete the `iterable` argument and leave the others untouched. // call. otherwise, we only delete the `iterable` argument and leave the others untouched.
let edit = if let Some(maxlen) = maxlen { let edit = if let Some(maxlen) = maxlen {
let deque_name = checker.locator().slice( let deque_name = checker.locator().slice(
parenthesized_range( parenthesized_range(deque.func.as_ref().into(), deque.into(), checker.tokens())
deque.func.as_ref().into(),
deque.into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(deque.func.range()), .unwrap_or(deque.func.range()),
); );
let len_str = checker.locator().slice(maxlen); let len_str = checker.locator().slice(maxlen);
let deque_str = format!("{deque_name}(maxlen={len_str})"); let deque_str = format!("{deque_name}(maxlen={len_str})");
Edit::range_replacement(deque_str, deque.range) Edit::range_replacement(deque_str, deque.range)
} else { } else {
let range = parenthesized_range(
iterable.value().into(),
(&deque.arguments).into(),
checker.comment_ranges(),
checker.source(),
)
.unwrap_or(iterable.range());
remove_argument( remove_argument(
&range, &iterable,
&deque.arguments, &deque.arguments,
Parentheses::Preserve, Parentheses::Preserve,
checker.source(), checker.source(),
checker.comment_ranges(), checker.tokens(),
)? )?
}; };
let has_comments = checker.comment_ranges().intersects(edit.range()); let has_comments = checker.comment_ranges().intersects(edit.range());

View File

@ -490,8 +490,10 @@ impl<'src> SuppressionParser<'src> {
} else if self.cursor.as_str().starts_with("enable") { } else if self.cursor.as_str().starts_with("enable") {
self.cursor.skip_bytes("enable".len()); self.cursor.skip_bytes("enable".len());
Ok(SuppressionAction::Enable) Ok(SuppressionAction::Enable)
} else if self.cursor.as_str().starts_with("noqa") { } else if self.cursor.as_str().starts_with("noqa")
// file-level "noqa" variant, ignore for now || self.cursor.as_str().starts_with("isort")
{
// alternate suppression variants, ignore for now
self.error(ParseErrorKind::NotASuppression) self.error(ParseErrorKind::NotASuppression)
} else { } else {
self.error(ParseErrorKind::UnknownAction) self.error(ParseErrorKind::UnknownAction)

View File

@ -3,13 +3,14 @@ use std::path::Path;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer, indentation_at_offset}; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, indentation_at_offset};
use ruff_source_file::LineRanges; use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::name::{Name, QualifiedName, QualifiedNameBuilder}; use crate::name::{Name, QualifiedName, QualifiedNameBuilder};
use crate::parenthesize::parenthesized_range;
use crate::statement_visitor::StatementVisitor; use crate::statement_visitor::StatementVisitor;
use crate::token::Tokens;
use crate::token::parenthesized_range;
use crate::visitor::Visitor; use crate::visitor::Visitor;
use crate::{ use crate::{
self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral, self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
@ -1474,7 +1475,7 @@ pub fn generate_comparison(
ops: &[CmpOp], ops: &[CmpOp],
comparators: &[Expr], comparators: &[Expr],
parent: AnyNodeRef, parent: AnyNodeRef,
comment_ranges: &CommentRanges, tokens: &Tokens,
source: &str, source: &str,
) -> String { ) -> String {
let start = left.start(); let start = left.start();
@ -1483,8 +1484,7 @@ pub fn generate_comparison(
// Add the left side of the comparison. // Add the left side of the comparison.
contents.push_str( contents.push_str(
&source[parenthesized_range(left.into(), parent, comment_ranges, source) &source[parenthesized_range(left.into(), parent, tokens).unwrap_or(left.range())],
.unwrap_or(left.range())],
); );
for (op, comparator) in ops.iter().zip(comparators) { for (op, comparator) in ops.iter().zip(comparators) {
@ -1504,7 +1504,7 @@ pub fn generate_comparison(
// Add the right side of the comparison. // Add the right side of the comparison.
contents.push_str( contents.push_str(
&source[parenthesized_range(comparator.into(), parent, comment_ranges, source) &source[parenthesized_range(comparator.into(), parent, tokens)
.unwrap_or(comparator.range())], .unwrap_or(comparator.range())],
); );
} }

View File

@ -36,7 +36,7 @@ impl Debug for DebugComment<'_> {
} }
} }
/// Pretty-printed debug representation of [`Comments`]. /// Pretty-printed debug representation of [`Comments`](super::Comments).
pub(crate) struct DebugComments<'a> { pub(crate) struct DebugComments<'a> {
comments: &'a CommentsMap<'a>, comments: &'a CommentsMap<'a>,
source_code: SourceCode<'a>, source_code: SourceCode<'a>,

View File

@ -504,7 +504,7 @@ impl InOrderEntry {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct OutOfOrderEntry { struct OutOfOrderEntry {
/// Index into the [`MultiMap::out_of_order`] vector at which offset the leading vec is stored. /// Index into the [`MultiMap::out_of_order_parts`] vector at which offset the leading vec is stored.
leading_index: usize, leading_index: usize,
_count: Count<OutOfOrderEntry>, _count: Count<OutOfOrderEntry>,
} }

View File

@ -2,7 +2,8 @@ use ruff_python_ast::AnyNodeRef;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
/// Used as key into the [`MultiMap`] storing the comments per node by [`Comments`]. /// Used as key into the [`MultiMap`](super::MultiMap) storing the comments per node by
/// [`Comments`](super::Comments).
/// ///
/// Implements equality and hashing based on the address of the [`AnyNodeRef`] to get fast and cheap /// Implements equality and hashing based on the address of the [`AnyNodeRef`] to get fast and cheap
/// hashing/equality comparison. /// hashing/equality comparison.

View File

@ -1974,8 +1974,8 @@ fn handle_unary_op_comment<'a>(
/// ) /// )
/// ``` /// ```
/// ///
/// The comment will be attached to the [`Arguments`] node as a dangling comment, to ensure /// The comment will be attached to the [`Arguments`](ast::Arguments) node as a dangling comment, to
/// that it remains on the same line as open parenthesis. /// ensure that it remains on the same line as open parenthesis.
/// ///
/// Similarly, given: /// Similarly, given:
/// ```python /// ```python
@ -1984,8 +1984,8 @@ fn handle_unary_op_comment<'a>(
/// ] = ... /// ] = ...
/// ``` /// ```
/// ///
/// The comment will be attached to the [`TypeParams`] node as a dangling comment, to ensure /// The comment will be attached to the [`TypeParams`](ast::TypeParams) node as a dangling comment,
/// that it remains on the same line as open bracket. /// to ensure that it remains on the same line as open bracket.
fn handle_bracketed_end_of_line_comment<'a>( fn handle_bracketed_end_of_line_comment<'a>(
comment: DecoratedComment<'a>, comment: DecoratedComment<'a>,
source: &str, source: &str,

View File

@ -174,7 +174,8 @@ impl<'ast> SourceOrderVisitor<'ast> for CommentsVisitor<'ast, '_> {
/// A comment decorated with additional information about its surrounding context in the source document. /// A comment decorated with additional information about its surrounding context in the source document.
/// ///
/// Used by [`CommentStyle::place_comment`] to determine if this should become a [leading](self#leading-comments), [dangling](self#dangling-comments), or [trailing](self#trailing-comments) comment. /// Used by [`place_comment`] to determine if this should become a [leading](self#leading-comments),
/// [dangling](self#dangling-comments), or [trailing](self#trailing-comments) comment.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct DecoratedComment<'a> { pub(crate) struct DecoratedComment<'a> {
enclosing: AnyNodeRef<'a>, enclosing: AnyNodeRef<'a>,
@ -465,7 +466,7 @@ pub(super) enum CommentPlacement<'a> {
/// ///
/// [`preceding_node`]: DecoratedComment::preceding_node /// [`preceding_node`]: DecoratedComment::preceding_node
/// [`following_node`]: DecoratedComment::following_node /// [`following_node`]: DecoratedComment::following_node
/// [`enclosing_node`]: DecoratedComment::enclosing_node_id /// [`enclosing_node`]: DecoratedComment::enclosing_node
/// [trailing comment]: self#trailing-comments /// [trailing comment]: self#trailing-comments
/// [leading comment]: self#leading-comments /// [leading comment]: self#leading-comments
/// [dangling comment]: self#dangling-comments /// [dangling comment]: self#dangling-comments

View File

@ -166,7 +166,7 @@ impl InterpolatedStringState {
} }
} }
/// Returns `true` if the interpolated string state is [`NestedInterpolatedElement`]. /// Returns `true` if the interpolated string state is [`Self::NestedInterpolatedElement`].
pub(crate) fn is_nested(self) -> bool { pub(crate) fn is_nested(self) -> bool {
matches!(self, Self::NestedInterpolatedElement(..)) matches!(self, Self::NestedInterpolatedElement(..))
} }

View File

@ -1095,9 +1095,9 @@ impl OperandIndex {
} }
} }
/// Returns the index of the operand's right operator. The method always returns an index /// Returns the index of the operand's right operator. The method always returns an index even
/// even if the operand has no right operator. Use [`BinaryCallChain::get_operator`] to test if /// if the operand has no right operator. Use [`FlatBinaryExpressionSlice::get_operator`] to
/// the operand has a right operator. /// test if the operand has a right operator.
fn right_operator(self) -> OperatorIndex { fn right_operator(self) -> OperatorIndex {
OperatorIndex::new(self.0 + 1) OperatorIndex::new(self.0 + 1)
} }

View File

@ -56,18 +56,20 @@ pub(crate) enum Parenthesize {
/// Adding parentheses is desired to prevent the comments from wandering. /// Adding parentheses is desired to prevent the comments from wandering.
IfRequired, IfRequired,
/// Same as [`Self::IfBreaks`] except that it uses [`parenthesize_if_expands`] for expressions /// Same as [`Self::IfBreaks`] except that it uses
/// with the layout [`NeedsParentheses::BestFit`] which is used by non-splittable /// [`parenthesize_if_expands`](crate::builders::parenthesize_if_expands) for expressions with
/// expressions like literals, name, and strings. /// the layout [`OptionalParentheses::BestFit`] which is used by non-splittable expressions like
/// literals, name, and strings.
/// ///
/// Use this layout over `IfBreaks` when there's a sequence of `maybe_parenthesize_expression` /// Use this layout over `IfBreaks` when there's a sequence of `maybe_parenthesize_expression`
/// in a single logical-line and you want to break from right-to-left. Use `IfBreaks` for the /// in a single logical-line and you want to break from right-to-left. Use `IfBreaks` for the
/// first expression and `IfBreaksParenthesized` for the rest. /// first expression and `IfBreaksParenthesized` for the rest.
IfBreaksParenthesized, IfBreaksParenthesized,
/// Same as [`Self::IfBreaksParenthesized`] but uses [`parenthesize_if_expands`] for nested /// Same as [`Self::IfBreaksParenthesized`] but uses
/// [`maybe_parenthesized_expression`] calls unlike other layouts that always omit parentheses /// [`parenthesize_if_expands`](crate::builders::parenthesize_if_expands) for nested
/// when outer parentheses are present. /// [`maybe_parenthesized_expression`](crate::expression::maybe_parenthesize_expression) calls
/// unlike other layouts that always omit parentheses when outer parentheses are present.
IfBreaksParenthesizedNested, IfBreaksParenthesizedNested,
} }

View File

@ -214,8 +214,9 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizePattern<'_> {
} }
} }
/// This function is very similar to [`can_omit_optional_parentheses`] with the only difference that it is for patterns /// This function is very similar to
/// and not expressions. /// [`can_omit_optional_parentheses`](crate::expression::can_omit_optional_parentheses)
/// with the only difference that it is for patterns and not expressions.
/// ///
/// The base idea of the omit optional parentheses layout is to prefer using parentheses of sub-patterns /// The base idea of the omit optional parentheses layout is to prefer using parentheses of sub-patterns
/// when splitting the pattern over introducing new patterns. For example, prefer splitting the sequence pattern in /// when splitting the pattern over introducing new patterns. For example, prefer splitting the sequence pattern in

View File

@ -72,8 +72,9 @@ impl FormatNodeRule<PatternArguments> for FormatPatternArguments {
} }
} }
/// Returns `true` if the pattern (which is the only argument to a [`PatternMatchClass`]) is /// Returns `true` if the pattern (which is the only argument to a
/// parenthesized. Used to avoid falsely assuming that `x` is parenthesized in cases like: /// [`PatternMatchClass`](ruff_python_ast::PatternMatchClass)) is parenthesized.
/// Used to avoid falsely assuming that `x` is parenthesized in cases like:
/// ```python /// ```python
/// case Point2D(x): ... /// case Point2D(x): ...
/// ``` /// ```

View File

@ -23,7 +23,8 @@ use crate::{FormatModuleError, PyFormatOptions, format_module_source};
/// ///
/// The returned formatted range guarantees to cover at least `range` (excluding whitespace), but the range might be larger. /// The returned formatted range guarantees to cover at least `range` (excluding whitespace), but the range might be larger.
/// Some cases in which the returned range is larger than `range` are: /// Some cases in which the returned range is larger than `range` are:
/// * The logical lines in `range` use a indentation different from the configured [`IndentStyle`] and [`IndentWidth`]. /// * The logical lines in `range` use a indentation different from the configured [`IndentStyle`]
/// and [`IndentWidth`](ruff_formatter::IndentWidth).
/// * `range` is smaller than a logical lines and the formatter needs to format the entire logical line. /// * `range` is smaller than a logical lines and the formatter needs to format the entire logical line.
/// * `range` falls on a single line body. /// * `range` falls on a single line body.
/// ///
@ -129,16 +130,19 @@ pub fn format_range(
/// b) formatting a sub-expression has fewer split points than formatting the entire expressions. /// b) formatting a sub-expression has fewer split points than formatting the entire expressions.
/// ///
/// ### Possible docstrings /// ### Possible docstrings
/// Strings that are suspected to be docstrings are excluded from the search to format the enclosing suite instead /// Strings that are suspected to be docstrings are excluded from the search to format the enclosing
/// so that the formatter's docstring detection in [`FormatSuite`] correctly detects and formats the docstrings. /// suite instead so that the formatter's docstring detection in
/// [`FormatSuite`](crate::statement::suite::FormatSuite) correctly detects and formats the
/// docstrings.
/// ///
/// ### Compound statements with a simple statement body /// ### Compound statements with a simple statement body
/// Don't include simple-statement bodies of compound statements `if True: pass` because the formatter /// Don't include simple-statement bodies of compound statements `if True: pass` because the formatter
/// must run [`FormatClauseBody`] to determine if the body should be collapsed or not. /// must run `FormatClauseBody` to determine if the body should be collapsed or not.
/// ///
/// ### Incorrectly indented code /// ### Incorrectly indented code
/// Code that uses indentations that don't match the configured [`IndentStyle`] and [`IndentWidth`] are excluded from the search, /// Code that uses indentations that don't match the configured [`IndentStyle`] and
/// because formatting such nodes on their own can lead to indentation mismatch with its sibling nodes. /// [`IndentWidth`](ruff_formatter::IndentWidth) are excluded from the search, because formatting
/// such nodes on their own can lead to indentation mismatch with its sibling nodes.
/// ///
/// ## Suppression comments /// ## Suppression comments
/// The search ends when `range` falls into a suppressed range because there's nothing to format. It also avoids that the /// The search ends when `range` falls into a suppressed range because there's nothing to format. It also avoids that the
@ -279,13 +283,15 @@ enum EnclosingNode<'a> {
/// ///
/// ## Compound statements with simple statement bodies /// ## Compound statements with simple statement bodies
/// Similar to [`find_enclosing_node`], exclude the compound statement's body if it is a simple statement (not a suite) from the search to format the entire clause header /// Similar to [`find_enclosing_node`], exclude the compound statement's body if it is a simple statement (not a suite) from the search to format the entire clause header
/// with the body. This ensures that the formatter runs [`FormatClauseBody`] that determines if the body should be indented.s /// with the body. This ensures that the formatter runs `FormatClauseBody` that determines if the body should be indented.
/// ///
/// ## Non-standard indentation /// ## Non-standard indentation
/// Node's that use an indentation that doesn't match the configured [`IndentStyle`] and [`IndentWidth`] are excluded from the search. /// Nodes that use an indentation that doesn't match the configured [`IndentStyle`] and
/// This is because the formatter always uses the configured [`IndentStyle`] and [`IndentWidth`], resulting in the /// [`IndentWidth`](ruff_formatter::IndentWidth) are excluded from the search. This is because the
/// formatted nodes using a different indentation than the unformatted sibling nodes. This would be tolerable /// formatter always uses the configured [`IndentStyle`] and
/// in non whitespace sensitive languages like JavaScript but results in lexical errors in Python. /// [`IndentWidth`](ruff_formatter::IndentWidth), resulting in the formatted nodes using a different
/// indentation than the unformatted sibling nodes. This would be tolerable in non whitespace
/// sensitive languages like JavaScript but results in lexical errors in Python.
/// ///
/// ## Implementation /// ## Implementation
/// It would probably be possible to merge this visitor with [`FindEnclosingNode`] but they are separate because /// It would probably be possible to merge this visitor with [`FindEnclosingNode`] but they are separate because
@ -713,9 +719,11 @@ impl Format<PyFormatContext<'_>> for FormatEnclosingNode<'_> {
} }
} }
/// Computes the level of indentation for `indentation` when using the configured [`IndentStyle`] and [`IndentWidth`]. /// Computes the level of indentation for `indentation` when using the configured [`IndentStyle`]
/// and [`IndentWidth`](ruff_formatter::IndentWidth).
/// ///
/// Returns `None` if the indentation doesn't conform to the configured [`IndentStyle`] and [`IndentWidth`]. /// Returns `None` if the indentation doesn't conform to the configured [`IndentStyle`] and
/// [`IndentWidth`](ruff_formatter::IndentWidth).
/// ///
/// # Panics /// # Panics
/// If `offset` is outside of `source`. /// If `offset` is outside of `source`.

View File

@ -184,7 +184,7 @@ impl Format<PyFormatContext<'_>> for FormatTargetWithEqualOperator<'_> {
/// No parentheses are added for `short` because it fits into the configured line length, regardless of whether /// No parentheses are added for `short` because it fits into the configured line length, regardless of whether
/// the comment exceeds the line width or not. /// the comment exceeds the line width or not.
/// ///
/// This logic isn't implemented in [`place_comment`] by associating trailing statement comments to the expression because /// This logic isn't implemented in `place_comment` by associating trailing statement comments to the expression because
/// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement. /// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement.
#[derive(Debug)] #[derive(Debug)]
pub(super) enum FormatStatementsLastExpression<'a> { pub(super) enum FormatStatementsLastExpression<'a> {
@ -202,8 +202,8 @@ pub(super) enum FormatStatementsLastExpression<'a> {
/// ] = some_long_value /// ] = some_long_value
/// ``` /// ```
/// ///
/// This layout is preferred over [`RightToLeft`] if the left is unsplittable (single keyword like `return` or a Name) /// This layout is preferred over [`Self::RightToLeft`] if the left is unsplittable (single
/// because it has better performance characteristics. /// keyword like `return` or a Name) because it has better performance characteristics.
LeftToRight { LeftToRight {
/// The right side of an assignment or the value returned in a return statement. /// The right side of an assignment or the value returned in a return statement.
value: &'a Expr, value: &'a Expr,
@ -1083,11 +1083,10 @@ impl Format<PyFormatContext<'_>> for InterpolatedString<'_> {
/// For legibility, we discuss only the case of f-strings below, but the /// For legibility, we discuss only the case of f-strings below, but the
/// same comments apply to t-strings. /// same comments apply to t-strings.
/// ///
/// This is just a wrapper around [`FormatFString`] while considering a special /// This is just a wrapper around [`FormatFString`](crate::other::f_string::FormatFString) while
/// case when the f-string is at an assignment statement's value position. /// considering a special case when the f-string is at an assignment statement's value position.
/// This is necessary to prevent an instability where an f-string contains a /// This is necessary to prevent an instability where an f-string contains a multiline expression
/// multiline expression and the f-string fits on the line, but only when it's /// and the f-string fits on the line, but only when it's surrounded by parentheses.
/// surrounded by parentheses.
/// ///
/// ```python /// ```python
/// aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ /// aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{

View File

@ -177,8 +177,10 @@ enum WithItemsLayout<'a> {
/// ... /// ...
/// ``` /// ```
/// ///
/// In this case, use [`maybe_parenthesize_expression`] to format the context expression /// In this case, use
/// to get the exact same formatting as when formatting an expression in any other clause header. /// [`maybe_parenthesize_expression`](crate::expression::maybe_parenthesize_expression) to
/// format the context expression to get the exact same formatting as when formatting an
/// expression in any other clause header.
/// ///
/// Only used for Python 3.9+ /// Only used for Python 3.9+
/// ///

View File

@ -783,7 +783,7 @@ enum CodeExampleKind<'src> {
/// ///
/// Documentation describing doctests and how they're recognized can be /// Documentation describing doctests and how they're recognized can be
/// found as part of the Python standard library: /// found as part of the Python standard library:
/// https://docs.python.org/3/library/doctest.html. /// <https://docs.python.org/3/library/doctest.html>.
/// ///
/// (You'll likely need to read the [regex matching] used internally by the /// (You'll likely need to read the [regex matching] used internally by the
/// doctest module to determine more precisely how it works.) /// doctest module to determine more precisely how it works.)

View File

@ -38,8 +38,9 @@ impl<'a, 'src> StringNormalizer<'a, 'src> {
/// it can't because the string contains the preferred quotes OR /// it can't because the string contains the preferred quotes OR
/// it leads to more escaping. /// it leads to more escaping.
/// ///
/// Note: If you add more cases here where we return `QuoteStyle::Preserve`, /// Note: If you add more cases here where we return `QuoteStyle::Preserve`, make sure to also
/// make sure to also add them to [`FormatImplicitConcatenatedStringFlat::new`]. /// add them to
/// [`FormatImplicitConcatenatedStringFlat::new`](crate::string::implicit::FormatImplicitConcatenatedStringFlat::new).
pub(super) fn preferred_quote_style(&self, string: StringLikePart) -> QuoteStyle { pub(super) fn preferred_quote_style(&self, string: StringLikePart) -> QuoteStyle {
let preferred_quote_style = self let preferred_quote_style = self
.preferred_quote_style .preferred_quote_style

View File

@ -9,7 +9,7 @@ use crate::prelude::*;
#[derive(Default)] #[derive(Default)]
pub struct FormatTypeParams; pub struct FormatTypeParams;
/// Formats a sequence of [`TypeParam`] nodes. /// Formats a sequence of [`TypeParam`](ruff_python_ast::TypeParam) nodes.
impl FormatNodeRule<TypeParams> for FormatTypeParams { impl FormatNodeRule<TypeParams> for FormatTypeParams {
fn fmt_fields(&self, item: &TypeParams, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &TypeParams, f: &mut PyFormatter) -> FormatResult<()> {
// A dangling comment indicates a comment on the same line as the opening bracket, e.g.: // A dangling comment indicates a comment on the same line as the opening bracket, e.g.:

View File

@ -679,8 +679,9 @@ impl Indentation {
/// Returns `true` for a space or tab character. /// Returns `true` for a space or tab character.
/// ///
/// This is different than [`is_python_whitespace`] in that it returns `false` for a form feed character. /// This is different than [`is_python_whitespace`](ruff_python_trivia::is_python_whitespace) in
/// Form feed characters are excluded because they should be preserved in the suppressed output. /// that it returns `false` for a form feed character. Form feed characters are excluded because
/// they should be preserved in the suppressed output.
const fn is_indent_whitespace(c: char) -> bool { const fn is_indent_whitespace(c: char) -> bool {
matches!(c, ' ' | '\t') matches!(c, ' ' | '\t')
} }

View File

@ -35,6 +35,7 @@ ruff_source_file = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
insta = { workspace = true, features = ["glob"] } insta = { workspace = true, features = ["glob"] }
itertools = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
walkdir = { workspace = true } walkdir = { workspace = true }

View File

@ -0,0 +1,5 @@
# Regression test for https://github.com/astral-sh/ty/issues/1828
(c: int = 1,f"""{d=[
def a(
class A:
pass

View File

@ -67,26 +67,59 @@ impl<'src> TokenSource<'src> {
/// ///
/// [`re_lex_logical_token`]: Lexer::re_lex_logical_token /// [`re_lex_logical_token`]: Lexer::re_lex_logical_token
pub(crate) fn re_lex_logical_token(&mut self) { pub(crate) fn re_lex_logical_token(&mut self) {
let mut non_logical_newline_start = None; let mut non_logical_newline = None;
for token in self.tokens.iter().rev() {
#[cfg(debug_assertions)]
let last_non_trivia_end_before = {
self.tokens
.iter()
.rev()
.find(|tok| !tok.kind().is_trivia())
.map(ruff_text_size::Ranged::end)
};
for (index, token) in self.tokens.iter().enumerate().rev() {
match token.kind() { match token.kind() {
TokenKind::NonLogicalNewline => { TokenKind::NonLogicalNewline => {
non_logical_newline_start = Some(token.start()); non_logical_newline = Some((index, token.start()));
} }
TokenKind::Comment => continue, TokenKind::Comment => continue,
_ => break, _ => break,
} }
} }
if self.lexer.re_lex_logical_token(non_logical_newline_start) { if !self
let current_start = self.current_range().start(); .lexer
while self .re_lex_logical_token(non_logical_newline.map(|(_, start)| start))
.tokens
.last()
.is_some_and(|last| last.start() >= current_start)
{ {
self.tokens.pop(); return;
} }
let non_logical_line_index = non_logical_newline
.expect(
"`re_lex_logical_token` should only return `true` if `non_logical_line` is `Some`",
)
.0;
// Trim the already bumped logical line token (and comments coming after it) as it might now have become a logical line token
self.tokens.truncate(non_logical_line_index);
#[cfg(debug_assertions)]
{
let last_non_trivia_end_now = {
self.tokens
.iter()
.rev()
.find(|tok| !tok.kind().is_trivia())
.map(ruff_text_size::Ranged::end)
};
assert_eq!(last_non_trivia_end_before, last_non_trivia_end_now);
}
// Ensure `current` is positioned at a non-trivia token.
if self.current_kind().is_trivia() {
self.bump(self.current_kind());
} }
} }

View File

@ -4,15 +4,16 @@ use std::fmt::{Formatter, Write};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use itertools::Itertools;
use ruff_annotate_snippets::{Level, Renderer, Snippet}; use ruff_annotate_snippets::{Level, Renderer, Snippet};
use ruff_python_ast::token::Token; use ruff_python_ast::token::{Token, Tokens};
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal, walk_module}; use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal, walk_module};
use ruff_python_ast::{self as ast, AnyNodeRef, Mod, PythonVersion}; use ruff_python_ast::{self as ast, AnyNodeRef, Mod, PythonVersion};
use ruff_python_parser::semantic_errors::{ use ruff_python_parser::semantic_errors::{
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError,
}; };
use ruff_python_parser::{Mode, ParseErrorType, ParseOptions, parse_unchecked}; use ruff_python_parser::{Mode, ParseErrorType, ParseOptions, Parsed, parse_unchecked};
use ruff_source_file::{LineIndex, OneIndexed, SourceCode}; use ruff_source_file::{LineIndex, OneIndexed, SourceCode};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
@ -81,7 +82,7 @@ fn test_valid_syntax(input_path: &Path) {
} }
validate_tokens(parsed.tokens(), source.text_len(), input_path); validate_tokens(parsed.tokens(), source.text_len(), input_path);
validate_ast(parsed.syntax(), source.text_len(), input_path); validate_ast(&parsed, source.text_len(), input_path);
let mut output = String::new(); let mut output = String::new();
writeln!(&mut output, "## AST").unwrap(); writeln!(&mut output, "## AST").unwrap();
@ -139,7 +140,7 @@ fn test_invalid_syntax(input_path: &Path) {
let parsed = parse_unchecked(&source, options.clone()); let parsed = parse_unchecked(&source, options.clone());
validate_tokens(parsed.tokens(), source.text_len(), input_path); validate_tokens(parsed.tokens(), source.text_len(), input_path);
validate_ast(parsed.syntax(), source.text_len(), input_path); validate_ast(&parsed, source.text_len(), input_path);
let mut output = String::new(); let mut output = String::new();
writeln!(&mut output, "## AST").unwrap(); writeln!(&mut output, "## AST").unwrap();
@ -402,12 +403,16 @@ Tokens: {tokens:#?}
/// * the range of the parent node fully encloses all its child nodes /// * the range of the parent node fully encloses all its child nodes
/// * the ranges are strictly increasing when traversing the nodes in pre-order. /// * the ranges are strictly increasing when traversing the nodes in pre-order.
/// * all ranges are within the length of the source code. /// * all ranges are within the length of the source code.
fn validate_ast(root: &Mod, source_len: TextSize, test_path: &Path) { fn validate_ast(parsed: &Parsed<Mod>, source_len: TextSize, test_path: &Path) {
walk_module(&mut ValidateAstVisitor::new(source_len, test_path), root); walk_module(
&mut ValidateAstVisitor::new(parsed.tokens(), source_len, test_path),
parsed.syntax(),
);
} }
#[derive(Debug)] #[derive(Debug)]
struct ValidateAstVisitor<'a> { struct ValidateAstVisitor<'a> {
tokens: std::iter::Peekable<std::slice::Iter<'a, Token>>,
parents: Vec<AnyNodeRef<'a>>, parents: Vec<AnyNodeRef<'a>>,
previous: Option<AnyNodeRef<'a>>, previous: Option<AnyNodeRef<'a>>,
source_length: TextSize, source_length: TextSize,
@ -415,8 +420,9 @@ struct ValidateAstVisitor<'a> {
} }
impl<'a> ValidateAstVisitor<'a> { impl<'a> ValidateAstVisitor<'a> {
fn new(source_length: TextSize, test_path: &'a Path) -> Self { fn new(tokens: &'a Tokens, source_length: TextSize, test_path: &'a Path) -> Self {
Self { Self {
tokens: tokens.iter().peekable(),
parents: Vec::new(), parents: Vec::new(),
previous: None, previous: None,
source_length, source_length,
@ -425,6 +431,47 @@ impl<'a> ValidateAstVisitor<'a> {
} }
} }
impl ValidateAstVisitor<'_> {
/// Check that the node's start doesn't fall within a token.
/// Called in `enter_node` before visiting children.
fn assert_start_boundary(&mut self, node: AnyNodeRef<'_>) {
// Skip tokens that end at or before the node starts.
self.tokens
.peeking_take_while(|t| t.end() <= node.start())
.last();
if let Some(next) = self.tokens.peek() {
// At this point, next_token.end() > node.start()
assert!(
next.start() >= node.start(),
"{path}: The start of the node falls within a token.\nNode: {node:#?}\n\nToken: {next:#?}\n\nRoot: {root:#?}",
path = self.test_path.display(),
root = self.parents.first()
);
}
}
/// Check that the node's end doesn't fall within a token.
/// Called in `leave_node` after visiting children, so all tokens
/// within the node have been consumed.
fn assert_end_boundary(&mut self, node: AnyNodeRef<'_>) {
// Skip tokens that end at or before the node ends.
self.tokens
.peeking_take_while(|t| t.end() <= node.end())
.last();
if let Some(next) = self.tokens.peek() {
// At this point, `next_token.end() > node.end()`
assert!(
next.start() >= node.end(),
"{path}: The end of the node falls within a token.\nNode: {node:#?}\n\nToken: {next:#?}\n\nRoot: {root:#?}",
path = self.test_path.display(),
root = self.parents.first()
);
}
}
}
impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> { impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
fn enter_node(&mut self, node: AnyNodeRef<'ast>) -> TraversalSignal { fn enter_node(&mut self, node: AnyNodeRef<'ast>) -> TraversalSignal {
assert!( assert!(
@ -452,12 +499,16 @@ impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
); );
} }
self.assert_start_boundary(node);
self.parents.push(node); self.parents.push(node);
TraversalSignal::Traverse TraversalSignal::Traverse
} }
fn leave_node(&mut self, node: AnyNodeRef<'ast>) { fn leave_node(&mut self, node: AnyNodeRef<'ast>) {
self.assert_end_boundary(node);
self.parents.pop().expect("Expected tree to be balanced"); self.parents.pop().expect("Expected tree to be balanced");
self.previous = Some(node); self.previous = Some(node);

View File

@ -296,7 +296,7 @@ Module(
test: Call( test: Call(
ExprCall { ExprCall {
node_index: NodeIndex(None), node_index: NodeIndex(None),
range: 456..472, range: 456..471,
func: Name( func: Name(
ExprName { ExprName {
node_index: NodeIndex(None), node_index: NodeIndex(None),
@ -306,7 +306,7 @@ Module(
}, },
), ),
arguments: Arguments { arguments: Arguments {
range: 460..472, range: 460..471,
node_index: NodeIndex(None), node_index: NodeIndex(None),
args: [ args: [
Name( Name(
@ -581,7 +581,7 @@ Module(
test: Call( test: Call(
ExprCall { ExprCall {
node_index: NodeIndex(None), node_index: NodeIndex(None),
range: 890..906, range: 890..905,
func: Name( func: Name(
ExprName { ExprName {
node_index: NodeIndex(None), node_index: NodeIndex(None),
@ -591,7 +591,7 @@ Module(
}, },
), ),
arguments: Arguments { arguments: Arguments {
range: 894..906, range: 894..905,
node_index: NodeIndex(None), node_index: NodeIndex(None),
args: [ args: [
FString( FString(
@ -832,7 +832,16 @@ Module(
| |
28 | # The lexer is nested with multiple levels of parentheses 28 | # The lexer is nested with multiple levels of parentheses
29 | if call(foo, [a, b 29 | if call(foo, [a, b
| ^ Syntax Error: Expected `]`, found NonLogicalNewline 30 | def bar():
| ^^^ Syntax Error: Expected `]`, found `def`
31 | pass
|
|
28 | # The lexer is nested with multiple levels of parentheses
29 | if call(foo, [a, b
| ^ Syntax Error: Expected `)`, found newline
30 | def bar(): 30 | def bar():
31 | pass 31 | pass
| |
@ -857,11 +866,10 @@ Module(
| |
41 | # test is to make sure it emits a `NonLogicalNewline` token after `b`.
42 | if call(foo, [a, 42 | if call(foo, [a,
43 | b 43 | b
| ^ Syntax Error: Expected `]`, found NonLogicalNewline
44 | ) 44 | )
| ^ Syntax Error: Expected `]`, found `)`
45 | def bar(): 45 | def bar():
46 | pass 46 | pass
| |
@ -898,7 +906,7 @@ Module(
| |
49 | # F-strings uses normal list parsing, so test those as well 49 | # F-strings uses normal list parsing, so test those as well
50 | if call(f"hello {x 50 | if call(f"hello {x
| ^ Syntax Error: Expected FStringEnd, found NonLogicalNewline | ^ Syntax Error: Expected `)`, found newline
51 | def bar(): 51 | def bar():
52 | pass 52 | pass
| |

View File

@ -17,7 +17,7 @@ Module(
test: Call( test: Call(
ExprCall { ExprCall {
node_index: NodeIndex(None), node_index: NodeIndex(None),
range: 3..19, range: 3..18,
func: Name( func: Name(
ExprName { ExprName {
node_index: NodeIndex(None), node_index: NodeIndex(None),
@ -27,7 +27,7 @@ Module(
}, },
), ),
arguments: Arguments { arguments: Arguments {
range: 7..19, range: 7..18,
node_index: NodeIndex(None), node_index: NodeIndex(None),
args: [ args: [
Name( Name(
@ -113,5 +113,11 @@ Module(
| |
1 | if call(foo, [a, b def bar(): pass 1 | if call(foo, [a, b def bar(): pass
| ^ Syntax Error: Expected `]`, found NonLogicalNewline | ^^^ Syntax Error: Expected `]`, found `def`
|
|
1 | if call(foo, [a, b def bar(): pass
| ^ Syntax Error: Expected `)`, found newline
| |

View File

@ -17,7 +17,7 @@ Module(
test: Call( test: Call(
ExprCall { ExprCall {
node_index: NodeIndex(None), node_index: NodeIndex(None),
range: 3..20, range: 3..18,
func: Name( func: Name(
ExprName { ExprName {
node_index: NodeIndex(None), node_index: NodeIndex(None),
@ -27,7 +27,7 @@ Module(
}, },
), ),
arguments: Arguments { arguments: Arguments {
range: 7..20, range: 7..18,
node_index: NodeIndex(None), node_index: NodeIndex(None),
args: [ args: [
Name( Name(
@ -113,7 +113,15 @@ Module(
| |
1 | if call(foo, [a, b 1 | if call(foo, [a, b
| ^ Syntax Error: Expected `]`, found NonLogicalNewline 2 | def bar():
| ^^^ Syntax Error: Expected `]`, found `def`
3 | pass
|
|
1 | if call(foo, [a, b
| ^ Syntax Error: Expected `)`, found newline
2 | def bar(): 2 | def bar():
3 | pass 3 | pass
| |

View File

@ -0,0 +1,227 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/invalid/re_lexing/ty_1828.py
---
## AST
```
Module(
ModModule {
node_index: NodeIndex(None),
range: 0..112,
body: [
AnnAssign(
StmtAnnAssign {
node_index: NodeIndex(None),
range: 66..93,
target: Name(
ExprName {
node_index: NodeIndex(None),
range: 67..68,
id: Name("c"),
ctx: Store,
},
),
annotation: Name(
ExprName {
node_index: NodeIndex(None),
range: 70..73,
id: Name("int"),
ctx: Load,
},
),
value: Some(
Tuple(
ExprTuple {
node_index: NodeIndex(None),
range: 76..93,
elts: [
NumberLiteral(
ExprNumberLiteral {
node_index: NodeIndex(None),
range: 76..77,
value: Int(
1,
),
},
),
Subscript(
ExprSubscript {
node_index: NodeIndex(None),
range: 78..90,
value: FString(
ExprFString {
node_index: NodeIndex(None),
range: 78..85,
value: FStringValue {
inner: Single(
FString(
FString {
range: 78..85,
node_index: NodeIndex(None),
elements: [
Interpolation(
InterpolatedElement {
range: 82..85,
node_index: NodeIndex(None),
expression: Name(
ExprName {
node_index: NodeIndex(None),
range: 83..84,
id: Name("d"),
ctx: Load,
},
),
debug_text: Some(
DebugText {
leading: "",
trailing: "=",
},
),
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: true,
unclosed: true,
},
},
),
),
},
},
),
slice: Slice(
ExprSlice {
node_index: NodeIndex(None),
range: 87..90,
lower: None,
upper: Some(
Name(
ExprName {
node_index: NodeIndex(None),
range: 87..90,
id: Name("def"),
ctx: Load,
},
),
),
step: None,
},
),
ctx: Load,
},
),
Call(
ExprCall {
node_index: NodeIndex(None),
range: 91..93,
func: Name(
ExprName {
node_index: NodeIndex(None),
range: 91..92,
id: Name("a"),
ctx: Load,
},
),
arguments: Arguments {
range: 92..93,
node_index: NodeIndex(None),
args: [],
keywords: [],
},
},
),
],
ctx: Load,
parenthesized: false,
},
),
),
simple: false,
},
),
],
},
)
```
## Errors
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
| ^ Syntax Error: Expected `)`, found `:`
3 | def a(
4 | class A:
|
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
| ^ Syntax Error: f-string: expecting `}`
3 | def a(
4 | class A:
|
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
3 | def a(
| ^^^ Syntax Error: Expected `:`, found `def`
4 | class A:
5 | pass
|
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
3 | def a(
| ^ Syntax Error: Expected `]`, found name
4 | class A:
5 | pass
|
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
3 | def a(
| _______^
4 | | class A:
5 | | pass
| |_________^ Syntax Error: f-string: unterminated triple-quoted string
|
|
2 | (c: int = 1,f"""{d=[
3 | def a(
4 | class A:
| ^^^^^ Syntax Error: Expected `)`, found `class`
5 | pass
|
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
2 | (c: int = 1,f"""{d=[
3 | def a(
| _______^
4 | | class A:
5 | | pass
| |_________^ Syntax Error: Expected a statement
|
|
4 | class A:
5 | pass
| ^ Syntax Error: unexpected EOF while parsing
|

View File

@ -1,7 +1,6 @@
# ty # ty
ty is an extremely fast type checker. ty is an extremely fast type checker.
Currently, it is a work-in-progress and not ready for production use.
The Rust code for ty lives in this repository; see [CONTRIBUTING.md](CONTRIBUTING.md) for more The Rust code for ty lives in this repository; see [CONTRIBUTING.md](CONTRIBUTING.md) for more
information on contributing to ty. information on contributing to ty.

148
crates/ty/docs/rules.md generated
View File

@ -39,7 +39,7 @@ def test(): -> "int":
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L135" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L134" target="_blank">View source</a>
</small> </small>
@ -63,7 +63,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L179" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L178" target="_blank">View source</a>
</small> </small>
@ -95,7 +95,7 @@ f(int) # error
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L205" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L204" target="_blank">View source</a>
</small> </small>
@ -126,7 +126,7 @@ a = 1
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L230" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L229" target="_blank">View source</a>
</small> </small>
@ -158,7 +158,7 @@ class C(A, B): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L256" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L255" target="_blank">View source</a>
</small> </small>
@ -190,7 +190,7 @@ class B(A): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) · Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-type-alias-definition" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-type-alias-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L282" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L281" target="_blank">View source</a>
</small> </small>
@ -218,7 +218,7 @@ type B = A
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L343" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L342" target="_blank">View source</a>
</small> </small>
@ -245,7 +245,7 @@ class B(A, A): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L364" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L363" target="_blank">View source</a>
</small> </small>
@ -357,7 +357,7 @@ def test(): -> "Literal[5]":
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L590" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L589" target="_blank">View source</a>
</small> </small>
@ -387,7 +387,7 @@ class C(A, B): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L614" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L613" target="_blank">View source</a>
</small> </small>
@ -413,7 +413,7 @@ t[3] # IndexError: tuple index out of range
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L396" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L395" target="_blank">View source</a>
</small> </small>
@ -502,7 +502,7 @@ an atypical memory layout.
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L668" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L667" target="_blank">View source</a>
</small> </small>
@ -529,7 +529,7 @@ func("foo") # error: [invalid-argument-type]
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L708" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L707" target="_blank">View source</a>
</small> </small>
@ -557,7 +557,7 @@ a: int = ''
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1998" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1997" target="_blank">View source</a>
</small> </small>
@ -591,7 +591,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L730" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L729" target="_blank">View source</a>
</small> </small>
@ -627,7 +627,7 @@ asyncio.run(main())
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L760" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L759" target="_blank">View source</a>
</small> </small>
@ -651,7 +651,7 @@ class A(42): ... # error: [invalid-base]
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L811" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L810" target="_blank">View source</a>
</small> </small>
@ -678,7 +678,7 @@ with 1:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L832" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L831" target="_blank">View source</a>
</small> </small>
@ -707,7 +707,7 @@ a: str
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L855" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L854" target="_blank">View source</a>
</small> </small>
@ -751,7 +751,7 @@ except ZeroDivisionError:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.28">0.0.1-alpha.28</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.28">0.0.1-alpha.28</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-explicit-override" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-explicit-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1668" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1667" target="_blank">View source</a>
</small> </small>
@ -793,7 +793,7 @@ class D(A):
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L891" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L890" target="_blank">View source</a>
</small> </small>
@ -826,7 +826,7 @@ class C[U](Generic[T]): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.17">0.0.1-alpha.17</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.17">0.0.1-alpha.17</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L635" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L634" target="_blank">View source</a>
</small> </small>
@ -865,7 +865,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L917" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L916" target="_blank">View source</a>
</small> </small>
@ -900,7 +900,7 @@ def f(t: TypeVar("U")): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1014" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1013" target="_blank">View source</a>
</small> </small>
@ -934,7 +934,7 @@ class B(metaclass=f): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-method-override" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-method-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2126" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2125" target="_blank">View source</a>
</small> </small>
@ -1041,7 +1041,7 @@ Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L542" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L541" target="_blank">View source</a>
</small> </small>
@ -1095,7 +1095,7 @@ AttributeError: Cannot overwrite NamedTuple attribute _asdict
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) · Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-newtype" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-newtype" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L990" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L989" target="_blank">View source</a>
</small> </small>
@ -1125,7 +1125,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1041" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1040" target="_blank">View source</a>
</small> </small>
@ -1175,7 +1175,7 @@ def foo(x: int) -> int: ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1140" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1139" target="_blank">View source</a>
</small> </small>
@ -1201,7 +1201,7 @@ def f(a: int = ''): ...
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-paramspec" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-paramspec" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L945" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L944" target="_blank">View source</a>
</small> </small>
@ -1232,7 +1232,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L478" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L477" target="_blank">View source</a>
</small> </small>
@ -1266,7 +1266,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1160" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1159" target="_blank">View source</a>
</small> </small>
@ -1315,7 +1315,7 @@ def g():
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L689" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L688" target="_blank">View source</a>
</small> </small>
@ -1340,7 +1340,7 @@ def func() -> int:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1203" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1202" target="_blank">View source</a>
</small> </small>
@ -1398,7 +1398,7 @@ TODO #14889
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.6">0.0.1-alpha.6</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.6">0.0.1-alpha.6</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L969" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L968" target="_blank">View source</a>
</small> </small>
@ -1425,7 +1425,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-arguments" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1435" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1434" target="_blank">View source</a>
</small> </small>
@ -1472,7 +1472,7 @@ Bar[int] # error: too few arguments
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1242" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1241" target="_blank">View source</a>
</small> </small>
@ -1502,7 +1502,7 @@ TYPE_CHECKING = ''
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1266" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1265" target="_blank">View source</a>
</small> </small>
@ -1532,7 +1532,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1318" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1317" target="_blank">View source</a>
</small> </small>
@ -1566,7 +1566,7 @@ f(10) # Error
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1289" target="_blank">View source</a>
</small> </small>
@ -1600,7 +1600,7 @@ class C:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1346" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1345" target="_blank">View source</a>
</small> </small>
@ -1635,7 +1635,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1375" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1374" target="_blank">View source</a>
</small> </small>
@ -1660,7 +1660,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2099" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2098" target="_blank">View source</a>
</small> </small>
@ -1693,7 +1693,7 @@ alice["age"] # KeyError
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1394" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1393" target="_blank">View source</a>
</small> </small>
@ -1722,7 +1722,7 @@ func("string") # error: [no-matching-overload]
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1417" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1416" target="_blank">View source</a>
</small> </small>
@ -1746,7 +1746,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1476" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1475" target="_blank">View source</a>
</small> </small>
@ -1772,7 +1772,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20override-of-final-method" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20override-of-final-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1641" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1640" target="_blank">View source</a>
</small> </small>
@ -1805,7 +1805,7 @@ class B(A):
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1527" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1526" target="_blank">View source</a>
</small> </small>
@ -1832,7 +1832,7 @@ f(1, x=2) # Error raised here
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20positional-only-parameter-as-kwarg" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20positional-only-parameter-as-kwarg" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1852" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1851" target="_blank">View source</a>
</small> </small>
@ -1890,7 +1890,7 @@ def test(): -> "int":
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1974" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1973" target="_blank">View source</a>
</small> </small>
@ -1920,7 +1920,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1618" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1617" target="_blank">View source</a>
</small> </small>
@ -1949,7 +1949,7 @@ class B(A): ... # Error raised here
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.30">0.0.1-alpha.30</a>) · Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.30">0.0.1-alpha.30</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20super-call-in-named-tuple-method" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20super-call-in-named-tuple-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1786" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1785" target="_blank">View source</a>
</small> </small>
@ -1983,7 +1983,7 @@ class F(NamedTuple):
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1726" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1725" target="_blank">View source</a>
</small> </small>
@ -2010,7 +2010,7 @@ f("foo") # Error raised here
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1704" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1703" target="_blank">View source</a>
</small> </small>
@ -2038,7 +2038,7 @@ def _(x: int):
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1747" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1746" target="_blank">View source</a>
</small> </small>
@ -2084,7 +2084,7 @@ class A:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1831" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1830" target="_blank">View source</a>
</small> </small>
@ -2111,7 +2111,7 @@ f(x=1, y=2) # Error raised here
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1873" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1872" target="_blank">View source</a>
</small> </small>
@ -2139,7 +2139,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1895" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1894" target="_blank">View source</a>
</small> </small>
@ -2164,7 +2164,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1914" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1913" target="_blank">View source</a>
</small> </small>
@ -2189,7 +2189,7 @@ print(x) # NameError: name 'x' is not defined
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1496" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1495" target="_blank">View source</a>
</small> </small>
@ -2226,7 +2226,7 @@ b1 < b2 < b1 # exception raised here
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1933" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1932" target="_blank">View source</a>
</small> </small>
@ -2254,7 +2254,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1955" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1954" target="_blank">View source</a>
</small> </small>
@ -2279,7 +2279,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L507" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L506" target="_blank">View source</a>
</small> </small>
@ -2320,7 +2320,7 @@ class SubProto(BaseProto, Protocol):
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.16">0.0.1-alpha.16</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.16">0.0.1-alpha.16</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L322" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L321" target="_blank">View source</a>
</small> </small>
@ -2408,7 +2408,7 @@ a = 20 / 0 # type: ignore
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1548" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1547" target="_blank">View source</a>
</small> </small>
@ -2436,7 +2436,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L153" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L152" target="_blank">View source</a>
</small> </small>
@ -2468,7 +2468,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1570" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1569" target="_blank">View source</a>
</small> </small>
@ -2500,7 +2500,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2026" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2025" target="_blank">View source</a>
</small> </small>
@ -2527,7 +2527,7 @@ cast(int, f()) # Redundant
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1813" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1812" target="_blank">View source</a>
</small> </small>
@ -2551,7 +2551,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.15">0.0.1-alpha.15</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.15">0.0.1-alpha.15</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2047" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2046" target="_blank">View source</a>
</small> </small>
@ -2609,7 +2609,7 @@ def g():
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.7">0.0.1-alpha.7</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.7">0.0.1-alpha.7</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L778" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L777" target="_blank">View source</a>
</small> </small>
@ -2648,7 +2648,7 @@ class D(C): ... # error: [unsupported-base]
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20useless-overload-body" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20useless-overload-body" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1084" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1083" target="_blank">View source</a>
</small> </small>
@ -2711,7 +2711,7 @@ def foo(x: int | str) -> int | str:
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a>) · Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L304" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L303" target="_blank">View source</a>
</small> </small>
@ -2735,7 +2735,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> · Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> · Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference" target="_blank">Related issues</a> · <a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1596" target="_blank">View source</a> <a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1595" target="_blank">View source</a>
</small> </small>

View File

@ -580,6 +580,53 @@ fn check_non_existing_path() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[test]
fn check_file_without_extension() -> anyhow::Result<()> {
let case = CliTest::with_file("main", "a = b")?;
assert_cmd_snapshot!(
case.command().arg("main"),
@r"
success: false
exit_code: 1
----- stdout -----
error[unresolved-reference]: Name `b` used when not defined
--> main:1:5
|
1 | a = b
| ^
|
info: rule `unresolved-reference` is enabled by default
Found 1 diagnostic
----- stderr -----
"
);
Ok(())
}
#[test]
fn check_file_without_extension_in_subfolder() -> anyhow::Result<()> {
let case = CliTest::with_file("src/main", "a = b")?;
assert_cmd_snapshot!(
case.command().arg("src"),
@r"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
WARN No python files found under the given path(s)
"
);
Ok(())
}
#[test] #[test]
fn concise_diagnostics() -> anyhow::Result<()> { fn concise_diagnostics() -> anyhow::Result<()> {
let case = CliTest::with_file( let case = CliTest::with_file(

View File

@ -6433,6 +6433,155 @@ collabc<CURSOR>
assert_snapshot!(snapshot, @"collections.abc"); assert_snapshot!(snapshot, @"collections.abc");
} }
#[test]
fn local_function_variable_with_return() {
let builder = completion_test_builder(
"\
variable_global = 1
def foo():
variable_local = 1
variable_<CURSOR>
return
",
);
assert_snapshot!(
builder.skip_auto_import().build().snapshot(),
@r"
variable_global
variable_local
",
);
}
#[test]
fn nested_scopes_with_return() {
let builder = completion_test_builder(
"\
variable_1 = 1
def fun1():
variable_2 = 1
def fun2():
variable_3 = 1
def fun3():
variable_4 = 1
variable_<CURSOR>
return
return
return
",
);
assert_snapshot!(
builder.skip_auto_import().build().snapshot(),
@r"
variable_1
variable_2
variable_3
variable_4
",
);
}
#[test]
fn multiple_declarations_global_scope1() {
let builder = completion_test_builder(
"\
zqzqzq: int = 1
zqzqzq: str = 'foo'
zqzq<CURSOR>
",
);
// The type for `zqzqzq` *should* be `str`, but we consider all
// reachable declarations and bindings, which means we get a
// union of `int` and `str` here even though the `int` binding
// isn't live at the cursor position.
assert_snapshot!(
builder.skip_auto_import().type_signatures().build().snapshot(),
@"zqzqzq :: int | str",
);
}
#[test]
fn multiple_declarations_global_scope2() {
let builder = completion_test_builder(
"\
zqzqzq: int = 1
zqzq<CURSOR>
zqzqzq: str = 'foo'
",
);
// The type for `zqzqzq` *should* be `int`, but we consider all
// reachable declarations and bindings, which means we get a
// union of `int` and `str` here even though the `str` binding
// doesn't exist at the cursor position.
assert_snapshot!(
builder.skip_auto_import().type_signatures().build().snapshot(),
@"zqzqzq :: int | str",
);
}
#[test]
fn multiple_declarations_function_scope1() {
let builder = completion_test_builder(
"\
def foo():
zqzqzq: int = 1
zqzqzq: str = 'foo'
zqzq<CURSOR>
return
",
);
// The type for `zqzqzq` *should* be `str`, but we consider all
// reachable declarations and bindings, which means we get a
// union of `int` and `str` here even though the `int` binding
// isn't live at the cursor position.
assert_snapshot!(
builder.skip_auto_import().type_signatures().build().snapshot(),
@"zqzqzq :: int | str",
);
}
#[test]
fn multiple_declarations_function_scope2() {
let builder = completion_test_builder(
"\
def foo():
zqzqzq: int = 1
zqzq<CURSOR>
zqzqzq: str = 'foo'
return
",
);
// The type for `zqzqzq` *should* be `int`, but we consider all
// reachable declarations and bindings, which means we get a
// union of `int` and `str` here even though the `str` binding
// doesn't exist at the cursor position.
assert_snapshot!(
builder.skip_auto_import().type_signatures().build().snapshot(),
@"zqzqzq :: int | str",
);
}
#[test]
fn multiple_declarations_function_parameter() {
let builder = completion_test_builder(
"\
from pathlib import Path
def f(zqzqzq: str):
zqzqzq: Path = Path(zqzqzq)
zqzq<CURSOR>
return
",
);
// The type for `zqzqzq` *should* be `Path`, but we consider all
// reachable declarations and bindings, which means we get a
// union of `str` and `Path` here even though the `str` binding
// isn't live at the cursor position.
assert_snapshot!(
builder.skip_auto_import().type_signatures().build().snapshot(),
@"zqzqzq :: str | Path",
);
}
/// A way to create a simple single-file (named `main.py`) completion test /// A way to create a simple single-file (named `main.py`) completion test
/// builder. /// builder.
/// ///

File diff suppressed because it is too large Load Diff

View File

@ -295,7 +295,7 @@ impl<'db> Definitions<'db> {
impl GotoTarget<'_> { impl GotoTarget<'_> {
pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> { pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
let ty = match self { match self {
GotoTarget::Expression(expression) => expression.inferred_type(model), GotoTarget::Expression(expression) => expression.inferred_type(model),
GotoTarget::FunctionDef(function) => function.inferred_type(model), GotoTarget::FunctionDef(function) => function.inferred_type(model),
GotoTarget::ClassDef(class) => class.inferred_type(model), GotoTarget::ClassDef(class) => class.inferred_type(model),
@ -317,7 +317,7 @@ impl GotoTarget<'_> {
} => { } => {
// We don't currently support hovering the bare `.` so there is always a name // We don't currently support hovering the bare `.` so there is always a name
let module = import_name(module_name, *component_index); let module = import_name(module_name, *component_index);
model.resolve_module_type(Some(module), *level)? model.resolve_module_type(Some(module), *level)
} }
GotoTarget::StringAnnotationSubexpr { GotoTarget::StringAnnotationSubexpr {
string_expr, string_expr,
@ -334,16 +334,16 @@ impl GotoTarget<'_> {
} else { } else {
// TODO: force the typechecker to tell us its secrets // TODO: force the typechecker to tell us its secrets
// (it computes but then immediately discards these types) // (it computes but then immediately discards these types)
return None; None
} }
} }
GotoTarget::BinOp { expression, .. } => { GotoTarget::BinOp { expression, .. } => {
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?; let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
ty Some(ty)
} }
GotoTarget::UnaryOp { expression, .. } => { GotoTarget::UnaryOp { expression, .. } => {
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?; let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
ty Some(ty)
} }
// TODO: Support identifier targets // TODO: Support identifier targets
GotoTarget::PatternMatchRest(_) GotoTarget::PatternMatchRest(_)
@ -353,10 +353,8 @@ impl GotoTarget<'_> {
| GotoTarget::TypeParamParamSpecName(_) | GotoTarget::TypeParamParamSpecName(_)
| GotoTarget::TypeParamTypeVarTupleName(_) | GotoTarget::TypeParamTypeVarTupleName(_)
| GotoTarget::NonLocal { .. } | GotoTarget::NonLocal { .. }
| GotoTarget::Globals { .. } => return None, | GotoTarget::Globals { .. } => None,
}; }
Some(ty)
} }
/// Try to get a simplified display of this callable type by resolving overloads /// Try to get a simplified display of this callable type by resolving overloads

Some files were not shown because too many files have changed in this diff Show More