c2rust-refactor 0.15.0

C2Rust refactoring tool implementation
'''This module generates `TryMatch` impls for each AST node type.

- Match two struct values by matching their corresponding fields.
- Match two enum values built from the same variant by matching their corresponding fields.  Enum
  values built from different variants fail to match.
- Ignore flag values - they always match.

Attributes:

- `#[match=custom]`: On a top-level declaration, skip generating an `impl` for this type, because
  one will be provided manually.  This is used for `Expr`, for example, since an `Expr` could in
  fact be a capturing pattern.

- `#[match=eq]`: On a top-level declaration, generate an `impl` that dispatches to `==`.

- `#[match=ignore]`: On a top-level declaration, generate a trivial impl that
  always returns true.  On a field, don't recurse on this field when matching.
'''

from datetime import datetime
from textwrap import indent, dedent

from ast import *
from util import *


@linewise
def do_match(se, target1, target2):
    yield 'match (%s, %s) {' % (target1, target2)
    for v, path in variants_paths(se):
        yield '  (&%s,' % struct_pattern(v, path, '1')
        yield '   &%s) => {' % struct_pattern(v, path, '2')
        for f in v.fields:
            if f.attrs.get('match') != 'ignore':
                yield '    mcx.try_match(%s1, %s2)?;' % \
                        (f.name, f.name)
        yield '    Ok(())'
        yield '  }'
    yield '  (_, _) => Err(matcher::Error::VariantMismatch),'
    yield '}'

@linewise
def compare_impl(se):
    yield '#[allow(unused)]'
    yield 'impl TryMatch for %s {' % se.name
    yield '  fn try_match(&self, target: &Self, mcx: &mut MatchCtxt) -> matcher::Result<()> {'
    yield indent(do_match(se, 'self', 'target'), '    ')
    yield '  }'
    yield '}'

@linewise
def eq_impl(d):
    yield '#[allow(unused)]'
    yield 'impl TryMatch for %s {' % d.name
    yield '  fn try_match(&self, target: &Self, _mcx: &mut MatchCtxt) -> matcher::Result<()> {'
    yield '    if self == target {'
    yield '      Ok(())'
    yield '    } else {'
    yield '      Err(matcher::Error::VariantMismatch)'
    yield '    }'
    yield '  }'
    yield '}'

@linewise
def ignore_impl(d):
    yield '#[allow(unused)]'
    yield 'impl TryMatch for %s {' % d.name
    yield '  fn try_match(&self, target: &Self, _mcx: &mut MatchCtxt) -> matcher::Result<()> {'
    yield '    Ok(())'
    yield '  }'
    yield '}'

@linewise
def custom_impl(se):
    yield '#[allow(unused)]'
    yield 'fn default_try_match_%s(this: &%s, target: &%s, mcx: &mut MatchCtxt) -> matcher::Result<()> {' % \
            (snake(se.name), se.name, se.name)
    yield indent(do_match(se, 'this', 'target'), '  ')
    yield '}'

@linewise
def generate(decls):
    yield '// AUTOMATICALLY GENERATED - DO NOT EDIT'
    yield '// Produced %s by process_ast.py' % (datetime.now(),)
    yield ''

    for d in decls:
        mode = d.attrs.get('match')
        if mode is None:
            if isinstance(d, (Struct, Enum)):
                mode = 'compare'
            else:
                mode = 'ignore'

        if mode == 'compare':
            yield compare_impl(d)
        elif mode == 'eq':
            yield eq_impl(d)
        elif mode == 'ignore':
            yield ignore_impl(d)
        elif mode == 'custom':
            if isinstance(d, (Struct, Enum)):
                yield custom_impl(d)
            # Otherwise, we don't need to provide a default fn