vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Syntactic meta-mutation discovery.

use syn::spanned::Spanned;
use syn::visit::Visit;

use super::{MetaMutation, MetaOperator};

/// Discover all possible syntactic mutations in a source file.
#[must_use]
#[inline]
pub fn discover(component: &'static str, source: &str) -> Vec<MetaMutation> {
    let syntax = match syn::parse_file(source) {
        Ok(s) => s,
        Err(_) => return Vec::new(),
    };

    let mut visitor = DiscoveryVisitor {
        component,
        mutations: Vec::new(),
    };
    visitor.visit_file(&syntax);
    visitor.mutations
}

struct DiscoveryVisitor {
    component: &'static str,
    mutations: Vec<MetaMutation>,
}

impl<'ast> Visit<'ast> for DiscoveryVisitor {
    fn visit_expr_binary(&mut self, i: &'ast syn::ExprBinary) {
        let span = i.op.span().start();
        match i.op {
            syn::BinOp::Eq(_) | syn::BinOp::Ne(_) | syn::BinOp::Lt(_) | syn::BinOp::Gt(_) => {
                self.mutations.push(MetaMutation::Syntactic {
                    component: self.component,
                    operator: MetaOperator::SwapComparison,
                    line: span.line,
                    column: span.column,
                });
            }
            _ => {}
        }

        match i.op {
            syn::BinOp::Lt(_) | syn::BinOp::Gt(_) => {
                self.mutations.push(MetaMutation::Syntactic {
                    component: self.component,
                    operator: MetaOperator::WeakenBound,
                    line: span.line,
                    column: span.column,
                });
            }
            _ => {}
        }
        syn::visit::visit_expr_binary(self, i);
    }

    fn visit_expr_macro(&mut self, i: &'ast syn::ExprMacro) {
        if i.mac.path.is_ident("assert") || i.mac.path.is_ident("assert_eq") {
            let span = i.mac.path.span().start();
            self.mutations.push(MetaMutation::Syntactic {
                component: self.component,
                operator: MetaOperator::DropAssertion,
                line: span.line,
                column: span.column,
            });
        }
        syn::visit::visit_expr_macro(self, i);
    }

    fn visit_lit_int(&mut self, i: &'ast syn::LitInt) {
        if let Ok(val) = i.base10_parse::<u64>() {
            if val == 0 || val == 1 {
                let span = i.span().start();
                self.mutations.push(MetaMutation::Syntactic {
                    component: self.component,
                    operator: MetaOperator::OffByOne,
                    line: span.line,
                    column: span.column,
                });
            }
        }
    }

    fn visit_expr_try(&mut self, i: &'ast syn::ExprTry) {
        let span = i.question_token.span.start();
        self.mutations.push(MetaMutation::Syntactic {
            component: self.component,
            operator: MetaOperator::SwallowError,
            line: span.line,
            column: span.column,
        });
        syn::visit::visit_expr_try(self, i);
    }
}