cfg_expr/
error.rs

1use std::{error::Error, fmt};
2
3/// An error related to parsing of a cfg expression
4#[derive(Debug, PartialEq, Eq)]
5pub struct ParseError {
6    /// The string that was parsed
7    pub original: String,
8    /// The range of characters in the original string that result
9    /// in this error
10    pub span: std::ops::Range<usize>,
11    /// The specific reason for the error
12    pub reason: Reason,
13}
14
15/// The particular reason for a `ParseError`
16#[derive(Debug, PartialEq, Eq)]
17pub enum Reason {
18    /// `not()` takes exactly 1 predicate, unlike `all()` and `any()`
19    InvalidNot(usize),
20    /// The characters are not valid in an cfg expression
21    InvalidCharacters,
22    /// An opening parens was unmatched with a closing parens
23    UnclosedParens,
24    /// A closing parens was unmatched with an opening parens
25    UnopenedParens,
26    /// An opening quotes was unmatched with a closing quotes
27    UnclosedQuotes,
28    /// A closing quotes was unmatched with an opening quotes
29    UnopenedQuotes,
30    /// The expression does not contain any valid terms
31    Empty,
32    /// Found an unexpected term, which wasn't one of the expected terms that
33    /// is listed
34    Unexpected(&'static [&'static str]),
35    /// Failed to parse an integer value
36    InvalidInteger,
37    /// The root `cfg()` may only contain a single predicate
38    MultipleRootPredicates,
39    /// A `target_has_atomic` predicate didn't correctly parse.
40    InvalidHasAtomic,
41    /// An element was not part of the builtin information in rustc
42    UnknownBuiltin,
43}
44
45impl fmt::Display for ParseError {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.write_str(&self.original)?;
48        f.write_str("\n")?;
49
50        for _ in 0..self.span.start {
51            f.write_str(" ")?;
52        }
53
54        // Mismatched parens/quotes have a slightly different output
55        // than the other errors
56        match &self.reason {
57            r @ (Reason::UnclosedParens | Reason::UnclosedQuotes) => {
58                f.write_fmt(format_args!("- {r}"))
59            }
60            r @ (Reason::UnopenedParens | Reason::UnopenedQuotes) => {
61                f.write_fmt(format_args!("^ {r}"))
62            }
63            other => {
64                for _ in self.span.start..self.span.end {
65                    f.write_str("^")?;
66                }
67
68                f.write_fmt(format_args!(" {other}"))
69            }
70        }
71    }
72}
73
74impl fmt::Display for Reason {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        use Reason::{
77            Empty, InvalidCharacters, InvalidHasAtomic, InvalidInteger, InvalidNot,
78            MultipleRootPredicates, UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin,
79            UnopenedParens, UnopenedQuotes,
80        };
81
82        match self {
83            InvalidCharacters => f.write_str("invalid character(s)"),
84            UnclosedParens => f.write_str("unclosed parens"),
85            UnopenedParens => f.write_str("unopened parens"),
86            UnclosedQuotes => f.write_str("unclosed quotes"),
87            UnopenedQuotes => f.write_str("unopened quotes"),
88            Empty => f.write_str("empty expression"),
89            Unexpected(expected) => {
90                if expected.len() > 1 {
91                    f.write_str("expected one of ")?;
92
93                    for (i, exp) in expected.iter().enumerate() {
94                        f.write_fmt(format_args!("{}`{exp}`", if i > 0 { ", " } else { "" }))?;
95                    }
96                    f.write_str(" here")
97                } else if !expected.is_empty() {
98                    f.write_fmt(format_args!("expected a `{}` here", expected[0]))
99                } else {
100                    f.write_str("the term was not expected here")
101                }
102            }
103            InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {np}")),
104            InvalidInteger => f.write_str("invalid integer"),
105            MultipleRootPredicates => f.write_str("multiple root predicates"),
106            InvalidHasAtomic => f.write_str("expected integer or \"ptr\""),
107            UnknownBuiltin => f.write_str("unknown built-in"),
108        }
109    }
110}
111
112impl Error for ParseError {
113    fn description(&self) -> &str {
114        use Reason::{
115            Empty, InvalidCharacters, InvalidHasAtomic, InvalidInteger, InvalidNot,
116            MultipleRootPredicates, UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin,
117            UnopenedParens, UnopenedQuotes,
118        };
119
120        match self.reason {
121            InvalidCharacters => "invalid character(s)",
122            UnclosedParens => "unclosed parens",
123            UnopenedParens => "unopened parens",
124            UnclosedQuotes => "unclosed quotes",
125            UnopenedQuotes => "unopened quotes",
126            Empty => "empty expression",
127            Unexpected(_) => "unexpected term",
128            InvalidNot(_) => "not() takes 1 predicate",
129            InvalidInteger => "invalid integer",
130            MultipleRootPredicates => "multiple root predicates",
131            InvalidHasAtomic => "expected integer or \"ptr\"",
132            UnknownBuiltin => "unknown built-in",
133        }
134    }
135}
136
137/// Error parsing a `target_has_atomic` predicate.
138#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct HasAtomicParseError {
140    pub(crate) input: String,
141}
142
143impl fmt::Display for HasAtomicParseError {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "expected integer or \"ptr\", found {}", self.input)
146    }
147}
148
149impl Error for HasAtomicParseError {}