error_handling.rs in compiler_interface.rs

·

2 min read


//! Compiler error parsing and handling to generate fixes
// 引入所需模块和库
use crate::{
    analysis,
    analysis::span::*,
    config::*,
    types::{Lifetime, Name},
};
use regex::Regex;
use serde::Deserialize;

// Diagnostic 结构用于存储编译器的错误信息和相关细节
#[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>,
}

// DiagnosticSpan 结构用于存储编译器错误的源代码位置信息
#[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>>,
}

// Applicability枚举用于标示建议的适用性
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
pub enum Applicability {
    MachineApplicable,
    HasPlaceholders,
    MaybeIncorrect,
    Unspecified,
}

// DiagnosticSpanLine结构用于存储源代码行的相关信息
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
pub struct DiagnosticSpanLine {
    pub text: String,
    pub highlight_start: usize,
    pub highlight_end: usize,
}

// DiagnosticSpanMacroExpansion结构用于存储宏扩展的相关信息
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
struct DiagnosticSpanMacroExpansion {
    span: DiagnosticSpan,
    macro_decl_name: String,
    def_site_span: Option<DiagnosticSpan>,
}

// DiagnosticCode结构用于存储编译器错误的代码和相关解释
#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
pub struct DiagnosticCode {
    pub code: String,
    explanation: Option<String>,
}

// generate_fixes 函数用于生成对编译器错误的修复
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();

    // Borrow the edit offsets
    let edit_offsets = EDIT_OFFSETS.lock().unwrap();
    // Regexes for extracting lifetimes from error messages for missing lifetime constraints
    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" => {
                    // 处理生命周期不匹配的类型错误
                    // Ensure that there is a single span
                    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
}