use crate::{
analysis,
analysis::span::*,
config::*,
types::{Lifetime, Name},
};
use regex::Regex;
use serde::Deserialize;
#[derive(Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
pub struct Diagnostic {
pub message: String,
pub code: Option<DiagnosticCode>,
level: String,
pub spans: Vec<DiagnosticSpan>,
pub children: Vec<Diagnostic>,
pub rendered: Option<String>,
}
#[derive(Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
pub struct DiagnosticSpan {
pub file_name: String,
pub byte_start: u32,
pub byte_end: u32,
pub line_start: usize,
pub line_end: usize,
pub column_start: usize,
pub column_end: usize,
is_primary: bool,
pub text: Vec<DiagnosticSpanLine>,
label: Option<String>,
pub suggested_replacement: Option<String>,
pub suggestion_applicability: Option<Applicability>,
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
}
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
pub enum Applicability {
MachineApplicable,
HasPlaceholders,
MaybeIncorrect,
Unspecified,
}
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
pub struct DiagnosticSpanLine {
pub text: String,
pub highlight_start: usize,
pub highlight_end: usize,
}
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
struct DiagnosticSpanMacroExpansion {
span: DiagnosticSpan,
macro_decl_name: String,
def_site_span: Option<DiagnosticSpan>,
}
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
pub struct DiagnosticCode {
pub code: String,
explanation: Option<String>,
}
pub fn generate_fixes(compiler_errors: &str) -> Configuration {
let diagnostics = serde_json::Deserializer::from_str(compiler_errors).into_iter::<Diagnostic>();
let mut cfg = Configuration::default();
let edit_offsets = EDIT_OFFSETS.lock().unwrap();
let lower_re =
Regex::new(r"the lifetime `(?P<lifetime>'[a-zA-Z0-9_]+)` as defined .*").unwrap();
let upper_re = Regex::new(
r"...does not necessarily outlive the lifetime `(?P<lifetime>'[a-zA-Z0-9_]+)` as defined .*",
)
.unwrap();
let span_info = analysis::result::<SpanInfo>().unwrap();
for diag in diagnostics {
let diagnostic = diag.unwrap();
if let Some(DiagnosticCode { code, .. }) = &diagnostic.code {
let rendered_error_message = diagnostic
.rendered
.as_ref()
.map(|s| s.as_ref())
.unwrap_or("<no detailed explanation>");
log::info!("processing error code {}", code);
match code.as_ref() {
"E0308" => {
assert_eq!(diagnostic.spans.len(), 1);
fn get_matching_lifetime_and_loc<'a>(
re: &'a Regex,
) -> Box<dyn Fn(&'a Diagnostic) -> Option<(Lifetime, FatSpan)> + 'a>
{
Box::new(move |d: &Diagnostic| {
re.captures(d.message.as_ref())
.and_then(|caps| caps.name("lifetime").map(|m| m.as_str()))
.map(|lifetime| {
assert_eq!(d.spans.len(), 1);
let diag_span = &d.spans[0];
let line_span = FatSpan {
file_name: Name::from(diag_span.file_name.as_str()),
begin: diag_span.byte_start as u32,
end: diag_span.byte_end as u32,
};
(Lifetime::from(lifetime), line_span)
})
})
};
let (lower, lower_span) = diagnostic
.children
.iter()
.find_map(get_matching_lifetime_and_loc(&lower_re))
.unwrap();
let (upper, upper_span) = diagnostic
.children
.iter()
.find_map(get_matching_lifetime_and_loc(&upper_re))
.unwrap();
let lower_span = edit_offsets.widest_origin_span(&lower_span);
let upper_span = edit_offsets.widest_origin_span(&upper_span);
let lower_fn = span_info.reverse_fn_spans.get(&lower_span).unwrap();
let upper_fn = span_info.reverse_fn_spans.get(&upper_span).unwrap();
assert_eq!(lower_fn, upper_fn);
let qual = Qual {
kind: QualKind::Fn,
q_name: lower_fn.clone(),
};
cfg.add_bound(qual, lower, upper);
},
_ => {
panic!(
"The diagnostic [{}] cannot be handled: {}. {}",
code, diagnostic.message, rendered_error_message
);
},
}
} else {
log::warn!("skipping diagnostic with message '{}'", diagnostic.message);
}
}
cfg
}