c2rust-refactor 0.15.0

C2Rust refactoring tool implementation
'''This module generates `GetSpan` impls for each AST node type that has a span.

- Structs with a field named `span` or a field with the `#[span]` attribute get
  a `GetSpan` impl that returns that span.
- Enums and flags do not have spans and thus do not get impls.

Attributes:

- `#[span]`: On a struct field, causes the field to be treated as the struct's
  span, even if its name is not `span`.  Only one field in a given struct can
  have this attribute.

- `#[no_span]`: On a struct, causes no impl to be generated, even if the struct
  has a field named `span`.

- `#[extend_span=field_name]`: Indicates that the struct's span should be
  extended to cover its attributes, which are stored in the indicated field.
  The field name can be omitted, in which case it defaults to `attrs`.
'''

from datetime import datetime
from textwrap import indent, dedent

from ast import *
from util import *


@linewise
def do_impl(s, field_name):
    yield 'impl GetSpan for %s {' % s.name
    yield '  fn get_span(&self) -> Span {'
    if 'extend_span' not in s.attrs:
        yield '    self.%s' % field_name
    else:
        attr_field = s.attrs['extend_span'] or 'attrs'
        yield '    extend_span_attrs(self.%s, &self.%s)' % (field_name, attr_field)
    yield '  }'
    yield '}'

def find_span_field(s):
    if 'no_span' in s.attrs:
        return None

    marked_fields = []
    for f in s.fields:
        if 'span' in f.attrs:
            marked_fields.append(f.name)
    if len(marked_fields) == 1:
        return marked_fields[0]
    elif len(marked_fields) > 1:
        raise ValueError('struct %s has %d fields marked #[span] (expected 0 or 1)' %
                (s.name, len(marked_fields)))

    for f in s.fields:
        if f.name == 'span':
            return f.name

    return None

@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 not isinstance(d, Struct):
            continue
        field_name = find_span_field(d)
        if field_name is not None:
            yield do_impl(d, field_name)

def has_get_span_impl(d):
    '''Returns `true` if type `d` implements `GetSpan`; `False` otherwise.'''
    return isinstance(d, Struct) and find_span_field(d) is not None