use std::{borrow::Cow, error, fmt};
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
InvalidExpression(ExpressionParseError),
UnknownTargetTriple(TripleParseError),
UnknownPlatformTriple(TripleParseError),
#[deprecated(since = "1.1.0", note = "this variant is no longer in use")]
#[doc(hidden)]
UnknownPredicate(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidExpression(_) => write!(f, "invalid cfg() expression"),
Error::UnknownTargetTriple(_) => write!(f, "unknown target triple"),
Error::UnknownPlatformTriple(_) => {
write!(f, "unknown platform triple")
}
#[allow(deprecated)]
Error::UnknownPredicate(pred) => {
write!(f, "cfg() expression has unknown predicate: {}", pred)
}
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::InvalidExpression(err) => Some(err),
Error::UnknownTargetTriple(err) => Some(err),
Error::UnknownPlatformTriple(err) => Some(err),
#[allow(deprecated)]
Error::UnknownPredicate(_) => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ExpressionParseError {
pub input: String,
pub span: std::ops::Range<usize>,
pub kind: ExpressionParseErrorKind,
}
impl ExpressionParseError {
pub(crate) fn new(input: &str, error: cfg_expr::ParseError) -> Self {
let span = if input.starts_with("cfg(") && input.ends_with(')') {
(error.span.start + 4)..(error.span.end + 4)
} else {
error.span
};
Self {
input: input.to_owned(),
span,
kind: ExpressionParseErrorKind::from_cfg_expr(error.reason),
}
}
}
impl fmt::Display for ExpressionParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "error parsing cfg() expression")
}
}
impl error::Error for ExpressionParseError {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExpressionParseErrorKind {
InvalidNot(usize),
InvalidCharacters,
UnclosedParens,
UnopenedParens,
UnclosedQuotes,
UnopenedQuotes,
Empty,
Unexpected {
expected: &'static [&'static str],
},
InvalidInteger,
MultipleRootPredicates,
InvalidHasAtomic,
UnknownBuiltin,
}
impl ExpressionParseErrorKind {
fn from_cfg_expr(reason: cfg_expr::error::Reason) -> Self {
use cfg_expr::error::Reason::*;
match reason {
InvalidCharacters => Self::InvalidCharacters,
UnclosedParens => Self::UnclosedParens,
UnopenedParens => Self::UnopenedParens,
UnclosedQuotes => Self::UnclosedQuotes,
UnopenedQuotes => Self::UnopenedQuotes,
Empty => Self::Empty,
Unexpected(expected) => Self::Unexpected { expected },
InvalidNot(np) => Self::InvalidNot(np),
InvalidInteger => Self::InvalidInteger,
MultipleRootPredicates => Self::MultipleRootPredicates,
InvalidHasAtomic => Self::InvalidHasAtomic,
UnknownBuiltin => Self::UnknownBuiltin,
}
}
}
impl fmt::Display for ExpressionParseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ExpressionParseErrorKind::*;
match self {
InvalidCharacters => f.write_str("invalid character(s)"),
UnclosedParens => f.write_str("unclosed parens"),
UnopenedParens => f.write_str("unopened parens"),
UnclosedQuotes => f.write_str("unclosed quotes"),
UnopenedQuotes => f.write_str("unopened quotes"),
Empty => f.write_str("empty expression"),
Unexpected { expected } => {
if expected.len() > 1 {
f.write_str("expected one of ")?;
for (i, exp) in expected.iter().enumerate() {
f.write_fmt(format_args!("{}`{exp}`", if i > 0 { ", " } else { "" }))?;
}
f.write_str(" here")
} else if !expected.is_empty() {
f.write_fmt(format_args!("expected a `{}` here", expected[0]))
} else {
f.write_str("the term was not expected here")
}
}
InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {np}")),
InvalidInteger => f.write_str("invalid integer"),
MultipleRootPredicates => f.write_str("multiple root predicates"),
InvalidHasAtomic => f.write_str("expected integer or \"ptr\""),
UnknownBuiltin => f.write_str("unknown built-in"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TripleParseError {
triple_str: Cow<'static, str>,
lexicon_err: cfg_expr::target_lexicon::ParseError,
}
impl TripleParseError {
pub(crate) fn new(
triple_str: Cow<'static, str>,
lexicon_err: cfg_expr::target_lexicon::ParseError,
) -> Self {
Self {
triple_str,
lexicon_err,
}
}
pub fn triple_str(&self) -> &str {
&self.triple_str
}
}
impl fmt::Display for TripleParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown triple string: {}", self.triple_str)
}
}
impl error::Error for TripleParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.lexicon_err)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TargetExpression;
use test_case::test_case;
#[test_case("cfg()", 4..4; "empty expression results in span inside cfg")]
#[test_case("target_os = \"macos", 12..18; "unclosed quote specified without cfg")]
fn test_expression_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
let err = match TargetExpression::new(input).unwrap_err() {
Error::InvalidExpression(err) => err,
other => {
panic!("unexpected error type {other:?}");
}
};
assert_eq!(err.span, expected_span);
}
}