python_ast/ast/tree/
raise_stmt.rs

1use proc_macro2::TokenStream;
2use pyo3::{Bound, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    CodeGen, CodeGenContext, ExprType, Node, PythonOptions, SymbolTableScopes,
8};
9
10/// Raise statement (raise [exception [from cause]])
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12pub struct Raise {
13    /// The exception to raise (optional - bare raise re-raises current exception)
14    pub exc: Option<ExprType>,
15    /// The cause of the exception (optional - used with 'from' clause)
16    pub cause: Option<ExprType>,
17    /// Position information
18    pub lineno: Option<usize>,
19    pub col_offset: Option<usize>,
20    pub end_lineno: Option<usize>,
21    pub end_col_offset: Option<usize>,
22}
23
24impl<'a> FromPyObject<'a> for Raise {
25    fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
26        // Extract exc (optional)
27        let exc: Option<ExprType> = if let Ok(exc_attr) = ob.getattr("exc") {
28            if exc_attr.is_none() {
29                None
30            } else {
31                Some(exc_attr.extract()?)
32            }
33        } else {
34            None
35        };
36        
37        // Extract cause (optional)
38        let cause: Option<ExprType> = if let Ok(cause_attr) = ob.getattr("cause") {
39            if cause_attr.is_none() {
40                None
41            } else {
42                Some(cause_attr.extract()?)
43            }
44        } else {
45            None
46        };
47        
48        Ok(Raise {
49            exc,
50            cause,
51            lineno: ob.lineno(),
52            col_offset: ob.col_offset(),
53            end_lineno: ob.end_lineno(),
54            end_col_offset: ob.end_col_offset(),
55        })
56    }
57}
58
59impl Node for Raise {
60    fn lineno(&self) -> Option<usize> { self.lineno }
61    fn col_offset(&self) -> Option<usize> { self.col_offset }
62    fn end_lineno(&self) -> Option<usize> { self.end_lineno }
63    fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
64}
65
66impl CodeGen for Raise {
67    type Context = CodeGenContext;
68    type Options = PythonOptions;
69    type SymbolTable = SymbolTableScopes;
70
71    fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
72        let symbols = if let Some(exc) = self.exc {
73            exc.find_symbols(symbols)
74        } else {
75            symbols
76        };
77        
78        if let Some(cause) = self.cause {
79            cause.find_symbols(symbols)
80        } else {
81            symbols
82        }
83    }
84
85    fn to_rust(
86        self,
87        ctx: Self::Context,
88        options: Self::Options,
89        symbols: Self::SymbolTable,
90    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
91        if let Some(exc) = self.exc {
92            let exc_tokens = exc.to_rust(ctx.clone(), options.clone(), symbols.clone())?;
93            
94            if let Some(cause) = self.cause {
95                let cause_tokens = cause.to_rust(ctx, options, symbols)?;
96                // For now, generate a simple panic since Rust doesn't have the same exception model
97                Ok(quote! {
98                    panic!("Exception: {:?} caused by {:?}", #exc_tokens, #cause_tokens)
99                })
100            } else {
101                Ok(quote! {
102                    panic!("Exception: {:?}", #exc_tokens)
103                })
104            }
105        } else {
106            // Bare raise - re-raises current exception
107            Ok(quote! {
108                panic!("Re-raising current exception")
109            })
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    // Tests would go here - currently commented out as they need full AST infrastructure
117    // create_parse_test!(test_simple_raise, "raise ValueError('error')", "test.py");
118}