Repository: astral-sh/ruff
File: crates/ruff_python_parser/src/semantic_errors.rs (Rust)

Code before:
}

#[derive(Default)]
struct ReturnVisitor {
    return_range: Option<TextRange>,
    has_yield: bool,
}

impl StatementVisitor<'_> for ReturnVisitor {
    fn visit_stmt(&mut self, stmt: &Stmt) {
        match stmt {
            Stmt::Expr(ast::StmtExpr { value, .. }) => match **value {
                Expr::Yield(_) | Expr::YieldFrom(_) => {
                    self.has_yield = true;
                }
                _ => {}
            },
            Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {}
            Stmt::Return(ast::StmtReturn {
                value: Some(_),
                range,
                ..
            }) => {
                if self.return_range.is_none() {
                    self.return_range = Some(*range);
                }
            }
            _ => statement_visitor::walk_stmt(self, stmt),
        }
    }
}

struct MatchPatternVisitor<'a, Ctx> {
    names: FxHashSet<&'a ast::name::Name>,
    ctx: &'a Ctx,
}

impl<'a, Ctx: SemanticSyntaxContext> MatchPatternVisitor<'a, Ctx> {
    fn visit_pattern(&mut self, pattern: &'a Pattern) {
        // test_ok class_keyword_in_case_pattern
        // match 2:
        //     case Class(x=x): ...

        // test_err multiple_assignment_in_case_pattern
        // match 2:
        //     case [y, z, y]: ...  # MatchSequence
        //     case [y, z, *y]: ...  # MatchSequence
        //     case [y, y, y]: ...  # MatchSequence multiple
        //     case {1: x, 2: x}: ...  # MatchMapping duplicate pattern
        //     case {1: x, **x}: ...  # MatchMapping duplicate in **rest
        //     case Class(x, x): ...  # MatchClass positional

Review comment (suggestion):
I like that this would take the first return like CPython does rather than the last return like B901, but I think for the sake of compatibility with B901, we should just take the last return and always overwrite:


```suggestion
                self.return_range = Some(*range);
```

Code after:

#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
pub enum InvalidExpressionKind {
    Yield,
    NamedExpr,
    Await,
}

impl Display for InvalidExpressionKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            InvalidExpressionKind::Yield => "yield expression",
            InvalidExpressionKind::NamedExpr => "named expression",
            InvalidExpressionKind::Await => "await expression",
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
pub enum WriteToDebugKind {
    Store,
    Delete(PythonVersion),
}

/// Searches for the first named expression (`x := y`) rebinding one of the `iteration_variables` in
/// a comprehension or generator expression.
struct ReboundComprehensionVisitor<'a> {
    comprehensions: &'a [ast::Comprehension],
    rebound_variables: Vec<TextRange>,
}

impl Visitor<'_> for ReboundComprehensionVisitor<'_> {
    fn visit_expr(&mut self, expr: &Expr) {
        if let Expr::Named(ast::ExprNamed { target, .. }) = expr {
            if let Expr::Name(ast::ExprName { id, range, .. }) = &**target {
                if self.comprehensions.iter().any(|comp| {
                    comp.target
                        .as_name_expr()
                        .is_some_and(|name| name.id == *id)
                }) {
                    self.rebound_variables.push(*range);
                }
            }
        }
        walk_expr(self, expr);
    }
}

#[derive(Default)]
struct ReturnVisitor {
    return_range: Option<TextRange>,