c2rust-refactor 0.15.0

C2Rust refactoring tool implementation
'''This module generates the `CollectMacros` impls for building the macro
collapser's `MacTable`.  This is a basic recursive traversal on two ASTs
(unexpanded and expanded), with a few special behaviors:

 * When the unexpanded AST is `Mac`, we call `record_one_macro` with the
   unexpanded `Mac` node and the corresponding expanded node's `NodeId`.  This
   check happens at nodes marked `#[mac_table_record]`, which should be the
   node with the ID (`Expr`, not `ExprKind`) and should implement `MaybeInvoc`.

 * On fields with the `#[mac_table_seq]` attribute, we call the helper function
   `collect_macros_seq` instead of the normal `collect_macros`, which handles
   macros possibly expanding into multiple sequential nodes.

Note that `#[mac_table_record]` should only be applied on nodes where a macro
expands to exactly one node.  Macros that can expand to multiple nodes need to
be handled by `#[mac_table_seq]` instead.  Leaving off `#[mac_table_record]`
for these nodes means we get a visible error if we forget the sequence
handling.


Attributes:

- `#[mac_table_record]`: Check for and record macros at each node of this type.

- `#[mac_table_seq]`: On a field, use `collect_macros_seq` to handle macros
  within the unexpanded sequence that produce multiple items in the expanded
  sequence.

- `#[mac_table_custom]`: Don't generate an impl for this type - a custom one
  will be provided elsewhere.
'''

from datetime import datetime
from textwrap import indent

from ast import *
from util import *

@linewise
def do_collect_macros_body(se, target1, target2):
    if not isinstance(se, (Struct, Enum)):
        return

    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 'mac_table_seq' in f.attrs:
                yield '    collect_macros_seq(%s1, %s2, cx);' % (f.name, f.name)
            else:
                yield '    CollectMacros::collect_macros(%s1, %s2, cx);' % (f.name, f.name)

        yield '  },'

    yield '  (_, _) => {},'
    yield '}'


    if 'mac_table_record' in se.attrs:
        yield 'if let Some(invoc) = MaybeInvoc::as_invoc(%s) {' % target1
        yield '  match MaybeInvoc::as_invoc(%s) {' % target2
        yield '    Some(InvocKind::Attrs(..)) => {}'
        yield '    Some(_) => panic!("impossible: found macro invocation in expanded AST"),'
        yield '    None => cx.record_one_macro(%s.id, invoc, MacNodeRef::%s(new)),' % \
                (target1, se.name)
        yield '  }'
        yield '}'

@linewise
def do_collect_macros_impl(d):
    yield '#[allow(unused)]'
    yield 'impl CollectMacros for %s {' % d.name
    yield "  fn collect_macros<'a>(old: &'a Self, new: &'a Self, cx: &mut Ctxt<'a>) {"
    yield indent(do_collect_macros_body(d, 'old', 'new'), '    ')
    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:
        if 'mac_table_custom' not in d.attrs:
            yield do_collect_macros_impl(d)