c2rust-refactor 0.15.0

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

- Two struct values are equivalent if their fields are equivalent.
- Two enum values are equivalent if they are built with the same variant and
  they have equivalent values in their corresponding fields.
- Two flag values are equivalent if they are equivalent under `==`.

Attributes:

- `#[equiv_mode=eq]`: On a type declaration, generate a trivial `impl` that compares
  values using `==`.

- `#[equiv_mode=ignore]`: On a type declaration, generate a trivial `impl` that
  always returns `true`.  The effect is that comparisons will ignore fields of
  this type.

- `#[equiv_mode=custom]`: On a type declaration, do not generate an `impl`.
'''

from datetime import datetime
from textwrap import indent, dedent

from ast import *
from util import *


@linewise
def exhaustiveness_check(se, target):
    yield 'match %s {' % target
    for v, path in variants_paths(se):
        yield '  &%s => {},' % struct_pattern(v, path)
    yield '}'

@linewise
def comparison(se, method, 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:
            yield '    AstEquiv::%s(%s1, %s2) &&' % \
                    (method, f.name, f.name)
        yield '    true'
        yield '  }'
    yield '  (_, _) => false,'
    yield '}'


@linewise
def compare_impl(se):
    yield '#[allow(unused, non_shorthand_field_patterns)]'
    yield 'impl AstEquiv for %s {' % se.name
    yield '  fn ast_equiv(&self, other: &Self) -> bool {'
    yield '    // Exhaustiveness check'
    yield indent(exhaustiveness_check(se, 'self'), '    ')
    yield ''
    yield '    // Comparison'
    yield indent(comparison(se, 'ast_equiv', 'self', 'other'), '    ')
    yield '  }'
    yield '  fn unnamed_equiv(&self, other: &Self) -> bool {'
    yield indent(comparison(se, 'unnamed_equiv', 'self', 'other'), '    ')
    yield '  }'
    yield '}'

@linewise
def eq_impl(d):
    yield '#[allow(unused)]'
    yield 'impl AstEquiv for %s {' % d.name
    yield '  fn ast_equiv(&self, other: &Self) -> bool {'
    yield '    self == other'
    yield '  }'
    yield '  fn unnamed_equiv(&self, other: &Self) -> bool {'
    yield '    self == other'
    yield '  }'
    yield '}'

@linewise
def ignore_impl(d):
    yield '#[allow(unused)]'
    yield 'impl AstEquiv for %s {' % d.name
    yield '  fn ast_equiv(&self, other: &Self) -> bool {'
    yield '    true'
    yield '  }'
    yield '  fn unnamed_equiv(&self, other: &Self) -> bool {'
    yield '    true'
    yield '  }'
    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('equiv_mode')
        if mode is None:
            if isinstance(d, (Struct, Enum)):
                mode = 'compare'
            else:
                mode = 'eq'

        if mode == 'compare':
            yield compare_impl(d)
        elif mode == 'eq':
            yield eq_impl(d)
        elif mode == 'ignore':
            yield ignore_impl(d)
        elif mode == 'custom':
            pass