Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/pylint/rules/stop_iteration_return.rs (Rust)

Code before:
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.14.3")]
pub(crate) struct StopIterationReturn;

impl Violation for StopIterationReturn {
    #[derive_message_formats]
    fn message(&self) -> String {
        "Explicit `raise StopIteration` in generator".to_string()
    }

    fn fix_title(&self) -> Option<String> {
        Some("Use `return` instead".to_string())
    }
}

/// PLR1708
pub(crate) fn stop_iteration_return(checker: &Checker, function_def: &ast::StmtFunctionDef) {
    let mut analyzer = GeneratorAnalyzer {
        checker,
        has_yield: false,
        stop_iteration_raises: Vec::new(),
    };

    for stmt in &function_def.body {
        analyzer.visit_stmt(stmt);
    }

    if analyzer.has_yield {
        for raise_stmt in analyzer.stop_iteration_raises {
            checker.report_diagnostic(StopIterationReturn, raise_stmt.range());
        }
    }
}

struct GeneratorAnalyzer<'a, 'b> {
    checker: &'a Checker<'b>,
    has_yield: bool,
    stop_iteration_raises: Vec<&'a ast::StmtRaise>,
}

impl<'a> Visitor<'a> for GeneratorAnalyzer<'a, '_> {
    fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
        match stmt {
            ast::Stmt::FunctionDef(_) => {}
            ast::Stmt::Raise(stmt_raise) => {
                if is_stop_iteration_raise(stmt_raise, self.checker) {
                    self.stop_iteration_raises.push(stmt_raise);
                }
                walk_stmt(self, stmt);
            }
            _ => walk_stmt(self, stmt),

Review comment (suggestion):
nit:
```suggestion
    analyzer.visit_body(&function_def.body);
```

Code after:
/// - [Python documentation](https://docs.python.org/3/library/exceptions.html#StopIteration)
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.14.3")]
pub(crate) struct StopIterationReturn;

impl Violation for StopIterationReturn {
    #[derive_message_formats]
    fn message(&self) -> String {
        "Explicit `raise StopIteration` in generator".to_string()
    }

    fn fix_title(&self) -> Option<String> {
        Some("Use `return` instead".to_string())
    }
}

/// PLR1708
pub(crate) fn stop_iteration_return(checker: &Checker, function_def: &ast::StmtFunctionDef) {
    let mut analyzer = GeneratorAnalyzer {
        checker,
        has_yield: false,
        stop_iteration_raises: Vec::new(),
    };

    analyzer.visit_body(&function_def.body);

    if analyzer.has_yield {
        for raise_stmt in analyzer.stop_iteration_raises {
            checker.report_diagnostic(StopIterationReturn, raise_stmt.range());
        }
    }
}

struct GeneratorAnalyzer<'a, 'b> {
    checker: &'a Checker<'b>,
    has_yield: bool,
    stop_iteration_raises: Vec<&'a ast::StmtRaise>,
}

impl<'a> Visitor<'a> for GeneratorAnalyzer<'a, '_> {
    fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
        match stmt {
            ast::Stmt::FunctionDef(_) => {}
            ast::Stmt::Raise(raise @ ast::StmtRaise { exc: Some(exc), .. }) => {
                if self
                    .checker
                    .semantic()
                    .match_builtin_expr(map_callable(exc), "StopIteration")
                {
                    self.stop_iteration_raises.push(raise);
                }