aidl_parser/
diagnostic.rs

1use crate::ast::Range;
2use crate::rules;
3use serde_derive::Serialize;
4
5#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
6pub struct Diagnostic {
7    pub kind: DiagnosticKind,
8    pub range: Range,
9    pub message: String,
10
11    /// Additional information displayed near the symbol
12    pub context_message: Option<String>,
13
14    pub hint: Option<String>,
15    pub related_infos: Vec<RelatedInfo>,
16}
17
18#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
19pub enum DiagnosticKind {
20    Error,
21    Warning,
22}
23
24#[derive(Serialize, Clone, Debug, PartialEq, Eq)]
25pub struct RelatedInfo {
26    pub range: Range,
27    pub message: String,
28}
29
30pub type ErrorRecovery<'input> =
31    lalrpop_util::ErrorRecovery<usize, rules::aidl::Token<'input>, &'static str>;
32
33pub type ParseError<'input> =
34    lalrpop_util::ParseError<usize, rules::aidl::Token<'input>, &'static str>;
35
36impl Diagnostic {
37    pub(crate) fn from_error_recovery(
38        msg: &str,
39        lookup: &line_col::LineColLookup,
40        error_recovery: ErrorRecovery,
41    ) -> Option<Diagnostic> {
42        Self::from_parse_error(lookup, error_recovery.error).map(|d| Diagnostic {
43            message: format!("{} - {}", msg, d.message),
44            ..d
45        })
46    }
47
48    pub(crate) fn from_parse_error(
49        lookup: &line_col::LineColLookup,
50        e: ParseError,
51    ) -> Option<Diagnostic> {
52        match e {
53            lalrpop_util::ParseError::InvalidToken { location } => Some(Diagnostic {
54                kind: DiagnosticKind::Error,
55                message: "Invalid token".to_owned(),
56                context_message: Some("invalid token".to_owned()),
57                range: Range::new(lookup, location, location),
58                hint: None,
59                related_infos: Vec::new(),
60            }),
61            lalrpop_util::ParseError::UnrecognizedEOF { location, expected } => Some(Diagnostic {
62                kind: DiagnosticKind::Error,
63                message: format!("Unrecognized EOF.\n{}", expected_token_str(&expected)),
64                context_message: Some("unrecognized EOF".to_owned()),
65                range: Range::new(lookup, location, location),
66                hint: None,
67                related_infos: Vec::new(),
68            }),
69            lalrpop_util::ParseError::UnrecognizedToken { token, expected } => Some(Diagnostic {
70                kind: DiagnosticKind::Error,
71                message: format!(
72                    "Unrecognized token `{}`.\n{}",
73                    token.1,
74                    expected_token_str(&expected)
75                ),
76                context_message: Some("unrecognized token".to_owned()),
77                range: Range::new(lookup, token.0, token.2),
78                hint: None,
79                related_infos: Vec::new(),
80            }),
81            lalrpop_util::ParseError::ExtraToken { token } => Some(Diagnostic {
82                kind: DiagnosticKind::Error,
83                message: format!("Extra token `{}`", token.1,),
84                context_message: Some("extra token".to_owned()),
85                range: Range::new(lookup, token.0, token.2),
86                hint: None,
87                related_infos: Vec::new(),
88            }),
89            lalrpop_util::ParseError::User { error: _ } => None, // User errors already produced a Diagnostic
90        }
91    }
92}
93
94// TODO: replace empty (or EOF?)!
95fn expected_token_str(v: &[String]) -> String {
96    match v.len() {
97        0 => String::new(),
98        1 => format!("Expected {}", v[0]),
99        2 => format!("Expected {} or {}", v[0], v[1]),
100        _ => format!(
101            "Expected one of {} or {}",
102            v[0..v.len() - 2].join(", "),
103            v[v.len() - 1]
104        ),
105    }
106}