File: access-note-gen.py

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (172 lines) | stat: -rw-r--r-- 5,544 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/env python3
# coding=utf8

"""
Convert selected @objc attributes in a source file into access notes, removing
the originals in the process.
"""

import io
import re
import sys

#
# Entry point
#


def main():
    if len(sys.argv) != 4:
        print('Too few args to ' + sys.argv[0])
        print('Usage: access-note-gen.py <input-file> <output-source-file> ' +
              '<output-access-notes-file>')
        sys.exit(1)

    with io.open(sys.argv[1], mode='r', encoding='utf8') as input_file, \
            io.open(sys.argv[2], mode='w', encoding='utf8') as output_file, \
            io.open(sys.argv[3], mode='w', encoding='utf8') as access_notes_file:

        # Add header to access notes file
        access_notes_file.write(u"""\
Reason: 'fancy tests'
Notes:""")

        # Loop over input lines, transforming them into output lines, writing access
        # notes as a side effect.
        for input_line in input_file:
            # Look for access-note-move comments.
            input_line = access_note_move_re.sub(replacer(move_at_objc_to_access_note,
                                                          access_notes_file),
                                                 input_line, count=1)

            # Look for access-note-adjust comments.
            input_line = adjust_comment_re.sub(replacer(adjust_comments),
                                               input_line, count=1)

            output_file.write(input_line)


#
# Offsets
#

"""Matches an @±N offset."""
offset_re_fragment = r'[ \t]*(?:@([+-]\d+))?[ \t]*'


def offsetify(*offsets):
    """Sum line offsets matched by offset_re_fragment and convert them to strings
       like @+3 or @-2."""

    offset = sum([int(o) for o in offsets if o is not None])
    if offset < 0:
        return u"@-" + str(-offset)
    elif offset > 0:
        return u"@+" + str(offset)
    else:
        return u""


#
# Adjusting comments
#

"""Matches expected-warning/note/remark and its offset."""
expected_other_diag_re = re.compile(r'expected-(warning|note|remark)' +
                                    offset_re_fragment)

"""Matches expected-error and its offset."""
expected_error_re = re.compile(r'expected-error' + offset_re_fragment +
                               r'\s*(\d*\s*)\{\{' +
                               r'([^}\\]*(?:(?:\}?\\.|\}[^}])[^}\\]*)*)' +
                               r'\}\}')

"""Matches the string 'marked @objc'."""
marked_objc_re = re.compile(r'marked @objc')

"""Matches any non-none fix-it expectation."""
fixit_re = re.compile(r'{{\d+-\d+=[^}]*}}')


def adjust_comments(offset, inserted_attr, comment_str):
    """Replace expected-errors with expected-remarks, and make other adjustments
       to diagnostics so that they reflect access notes."""

    prefix = u"{{ignored access note: "
    suffix = u"; did not implicitly add '" + inserted_attr + "' to this }}"

    adjusted = expected_other_diag_re.sub(lambda m: u"expected-" + m.group(1) +
                                                    offsetify(offset, m.group(2)),
                                          comment_str)
    adjusted = expected_error_re.sub(lambda m: u"expected-remark" +
                                               offsetify(offset, m.group(1)) + " " +
                                               m.group(2) + prefix + m.group(3) +
                                               suffix,
                                     adjusted)
    adjusted = marked_objc_re.sub(u"marked @objc by an access note", adjusted)
    adjusted = fixit_re.sub(u"{{none}}", adjusted)

    return u"// [expectations adjusted] " + adjusted


#
# Writing attrs to access notes
#

def move_at_objc_to_access_note(access_notes_file, arg, maybe_bad, offset,
                                access_note_name):
    """Write an @objc attribute into an access notes file, then return the
       string that will replace the attribute and trailing comment."""

    is_bad = (maybe_bad == "bad-")

    access_notes_file.write(u"""
- Name: '{}'
  ObjC: true""".format(access_note_name))
    if arg:
        access_notes_file.write(u"""
  ObjCName: '{}'""".format(arg))

    # Default to shifting expected diagnostics down 1 line.
    if offset is None:
        offset = 1

    inserted_attr = u"@objc"
    if arg:
        inserted_attr += u"(" + arg + u")"

    replacement = u"// access-note-adjust" + offsetify(offset) + \
                  u"{{" + inserted_attr + "}} [attr moved] "

    if not is_bad:
        replacement += u"expected-remark{{implicitly added '" + inserted_attr + \
                       u"' to this }} expected-note{{add '" + inserted_attr + \
                       u"' explicitly to silence this warning}}"

    return replacement


#
# Matching lines
#

"""Matches '@objc(foo) // access-note-move{{access-note-name}}'
   or '@objc // bad-access-note-move{{access-note-name}}'"""
access_note_move_re = re.compile(r'@objc(?:\(([\w:]+)\))?[ \t]*' +
                                 r'//[ \t]*(bad-)?access-note-move' +
                                 offset_re_fragment +
                                 r'\{\{([^}]*)\}\}')

"""Matches // access-note-adjust{{@objc}} <comment>"""
adjust_comment_re = re.compile(r'//[ \t]*access-note-adjust' + offset_re_fragment +
                               r'\{\{([^}]*)\}\}[ \t]*(.*)')


def replacer(fn, *args):
    """Returns a lambda which calls fn with args, followed by the groups from
       the match passed to the lambda."""

    return lambda m: fn(*(args + m.groups()))


main()