Skip to main content

pcode_types/
error.rs

1//! Errors raised while lowering and expanding SLEIGH p-code semantics.
2//!
3//! These are SLEIGH-language errors — a malformed `macro` body, an unresolvable
4//! expression size — as distinct from errors about the IR a consumer builds
5//! afterwards. They carry an optional byte span into the preprocessed source.
6
7use std::{fmt::Display, ops::Range};
8
9/// The kind of a [`PcodeError`].
10#[derive(Debug, PartialEq, Eq)]
11pub enum PcodeErrorTy {
12    /// A bit range extends past the end of the value it indexes.
13    RangeOutOfBounds {
14        /// The bit range that was asked for.
15        range: Range<usize>,
16        /// How many bits the indexed value actually has.
17        ///
18        /// [`PcodeError::range_out_of_bounds`] has no size to hand and leaves
19        /// this zero.
20        available: usize,
21    },
22
23    /// A macro was invoked with the wrong number of arguments.
24    ArgumentCountMismatch {
25        /// Number of parameters in the macro's definition.
26        expected: usize,
27        /// Number of arguments at the call site.
28        actual: usize,
29    },
30
31    /// Could not determine the size of an expression.
32    UnknownSize,
33
34    /// A macro was invoked but never defined.
35    UnknownMacro(Box<str>),
36
37    /// A macro definition contains more than one `export`.
38    MultipleExports,
39
40    /// The `export` statement is not the last statement in a macro definition.
41    ExportNotLast,
42
43    /// A statement-only construct was used where an expression was expected.
44    FunctionStatement,
45
46    /// Valid SLEIGH that this crate does not implement.
47    Unsupported(Box<str>),
48}
49
50/// An error raised while lowering SLEIGH p-code, with an optional source span.
51#[derive(Debug)]
52pub struct PcodeError {
53    /// What went wrong.
54    pub ty: PcodeErrorTy,
55    /// Byte range `(start, end)` into the prepared source, if available.
56    pub span: Option<(usize, usize)>,
57}
58
59/// Shorthand for a result carrying a [`PcodeError`].
60pub type PcodeResult<T> = std::result::Result<T, PcodeError>;
61
62impl std::error::Error for PcodeError {}
63
64/// Spans are diagnostic detail, not identity: two errors of the same kind
65/// compare equal regardless of where they were raised.
66impl PartialEq for PcodeError {
67    fn eq(&self, other: &Self) -> bool {
68        self.ty == other.ty
69    }
70}
71
72impl Eq for PcodeError {}
73
74impl PcodeError {
75    /// Creates an error carrying a source span.
76    pub fn new(ty: PcodeErrorTy, span: (usize, usize)) -> Self {
77        Self {
78            ty,
79            span: Some(span),
80        }
81    }
82
83    /// Creates an error with no source span.
84    pub fn spanless(ty: PcodeErrorTy) -> Self {
85        Self { ty, span: None }
86    }
87
88    /// Attaches a source span, replacing any existing one.
89    pub fn with_span(mut self, span: (usize, usize)) -> Self {
90        self.span = Some(span);
91        self
92    }
93
94    /// A bit range that extends past the end of its subject.
95    pub fn range_out_of_bounds(range: Range<usize>, span: (usize, usize)) -> Self {
96        Self::new(
97            PcodeErrorTy::RangeOutOfBounds {
98                range,
99                available: 0,
100            },
101            span,
102        )
103    }
104
105    /// A macro invoked with the wrong number of arguments.
106    pub fn argument_count_mismatch(expected: usize, actual: usize, span: (usize, usize)) -> Self {
107        Self::new(
108            PcodeErrorTy::ArgumentCountMismatch { expected, actual },
109            span,
110        )
111    }
112
113    /// An expression whose size could not be determined.
114    pub fn unknown_size(span: (usize, usize)) -> Self {
115        Self::new(PcodeErrorTy::UnknownSize, span)
116    }
117
118    /// A macro invoked but never defined.
119    pub fn unknown_macro(name: &str, span: (usize, usize)) -> Self {
120        Self::new(PcodeErrorTy::UnknownMacro(name.into()), span)
121    }
122
123    /// A macro definition containing more than one `export`.
124    pub fn multiple_exports(span: (usize, usize)) -> Self {
125        Self::new(PcodeErrorTy::MultipleExports, span)
126    }
127
128    /// An `export` that is not the last statement in its macro definition.
129    pub fn export_not_last(span: (usize, usize)) -> Self {
130        Self::new(PcodeErrorTy::ExportNotLast, span)
131    }
132
133    /// A statement-only construct used where an expression was expected.
134    pub fn function_is_a_statement(span: (usize, usize)) -> Self {
135        Self::new(PcodeErrorTy::FunctionStatement, span)
136    }
137}
138
139impl Display for PcodeError {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        let message = match &self.ty {
142            PcodeErrorTy::RangeOutOfBounds { range, available } => {
143                format!("Range {range:?} is out of bounds for available size {available}")
144            }
145
146            PcodeErrorTy::ArgumentCountMismatch { expected, actual } => {
147                format!("Expected {expected} arguments but got {actual}")
148            }
149
150            PcodeErrorTy::UnknownSize => {
151                "Could not determine the size of this expression".to_string()
152            }
153
154            PcodeErrorTy::UnknownMacro(name) => format!("Unknown macro: {name}"),
155
156            PcodeErrorTy::MultipleExports => {
157                "A macro definition contains multiple exports".to_string()
158            }
159
160            PcodeErrorTy::ExportNotLast => {
161                "The export statement is not the last statement in a macro definition".to_string()
162            }
163
164            PcodeErrorTy::FunctionStatement => {
165                "Attempted to use a function as an expression, but it is a statement".to_string()
166            }
167
168            PcodeErrorTy::Unsupported(what) => what.to_string(),
169        };
170
171        if let Some((start, end)) = self.span {
172            write!(f, "{message} (bytes {start}..{end})")
173        } else {
174            write!(f, "{message}")
175        }
176    }
177}