1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
use crate::{
nfa,
util::{
id::{PatternID, StateID},
start::Start,
},
};
/// An error that occurred during the construction of a DFA.
///
/// This error does not provide many introspection capabilities. There are
/// generally only two things you can do with it:
///
/// * Obtain a human readable message via its `std::fmt::Display` impl.
/// * Access an underlying [`nfa::thompson::Error`] type from its `source`
/// method via the `std::error::Error` trait. This error only occurs when using
/// convenience routines for building a DFA directly from a pattern string.
///
/// When the `std` feature is enabled, this implements the `std::error::Error`
/// trait.
#[derive(Clone, Debug)]
pub struct Error {
kind: ErrorKind,
}
/// The kind of error that occurred during the construction of a DFA.
///
/// Note that this error is non-exhaustive. Adding new variants is not
/// considered a breaking change.
#[derive(Clone, Debug)]
enum ErrorKind {
/// An error that occurred while constructing an NFA as a precursor step
/// before a DFA is compiled.
NFA(nfa::thompson::Error),
/// An error that occurred because an unsupported regex feature was used.
/// The message string describes which unsupported feature was used.
///
/// The primary regex feature that is unsupported by DFAs is the Unicode
/// word boundary look-around assertion (`\b`). This can be worked around
/// by either using an ASCII word boundary (`(?-u:\b)`) or by enabling the
/// [`dense::Builder::allow_unicode_word_boundary`](dense/struct.Builder.html#method.allow_unicode_word_boundary)
/// option when building a DFA.
Unsupported(&'static str),
/// An error that occurs if too many states are produced while building a
/// DFA.
TooManyStates,
/// An error that occurs if too many start states are needed while building
/// a DFA.
///
/// This is a kind of oddball error that occurs when building a DFA with
/// start states enabled for each pattern and enough patterns to cause
/// the table of start states to overflow `usize`.
TooManyStartStates,
/// This is another oddball error that can occur if there are too many
/// patterns spread out across too many match states.
TooManyMatchPatternIDs,
/// An error that occurs if the DFA got too big during determinization.
DFAExceededSizeLimit { limit: usize },
/// An error that occurs if auxiliary storage (not the DFA) used during
/// determinization got too big.
DeterminizeExceededSizeLimit { limit: usize },
}
impl Error {
/// Return the kind of this error.
fn kind(&self) -> &ErrorKind {
&self.kind
}
pub(crate) fn nfa(err: nfa::thompson::Error) -> Error {
Error { kind: ErrorKind::NFA(err) }
}
pub(crate) fn unsupported_dfa_word_boundary_unicode() -> Error {
let msg = "cannot build DFAs for regexes with Unicode word \
boundaries; switch to ASCII word boundaries, or \
heuristically enable Unicode word boundaries or use a \
different regex engine";
Error { kind: ErrorKind::Unsupported(msg) }
}
pub(crate) fn too_many_states() -> Error {
Error { kind: ErrorKind::TooManyStates }
}
pub(crate) fn too_many_start_states() -> Error {
Error { kind: ErrorKind::TooManyStartStates }
}
pub(crate) fn too_many_match_pattern_ids() -> Error {
Error { kind: ErrorKind::TooManyMatchPatternIDs }
}
pub(crate) fn dfa_exceeded_size_limit(limit: usize) -> Error {
Error { kind: ErrorKind::DFAExceededSizeLimit { limit } }
}
pub(crate) fn determinize_exceeded_size_limit(limit: usize) -> Error {
Error { kind: ErrorKind::DeterminizeExceededSizeLimit { limit } }
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.kind() {
ErrorKind::NFA(ref err) => Some(err),
ErrorKind::Unsupported(_) => None,
ErrorKind::TooManyStates => None,
ErrorKind::TooManyStartStates => None,
ErrorKind::TooManyMatchPatternIDs => None,
ErrorKind::DFAExceededSizeLimit { .. } => None,
ErrorKind::DeterminizeExceededSizeLimit { .. } => None,
}
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.kind() {
ErrorKind::NFA(_) => write!(f, "error building NFA"),
ErrorKind::Unsupported(ref msg) => {
write!(f, "unsupported regex feature for DFAs: {}", msg)
}
ErrorKind::TooManyStates => write!(
f,
"number of DFA states exceeds limit of {}",
StateID::LIMIT,
),
ErrorKind::TooManyStartStates => {
let stride = Start::count();
// The start table has `stride` entries for starting states for
// the entire DFA, and then `stride` entries for each pattern
// if start states for each pattern are enabled (which is the
// only way this error can occur). Thus, the total number of
// patterns that can fit in the table is `stride` less than
// what we can allocate.
let limit = ((core::isize::MAX as usize) - stride) / stride;
write!(
f,
"compiling DFA with start states exceeds pattern \
pattern limit of {}",
limit,
)
}
ErrorKind::TooManyMatchPatternIDs => write!(
f,
"compiling DFA with total patterns in all match states \
exceeds limit of {}",
PatternID::LIMIT,
),
ErrorKind::DFAExceededSizeLimit { limit } => write!(
f,
"DFA exceeded size limit of {:?} during determinization",
limit,
),
ErrorKind::DeterminizeExceededSizeLimit { limit } => {
write!(f, "determinization exceeded size limit of {:?}", limit)
}
}
}
}