-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathaccess-note-gen.py
153 lines (111 loc) · 4.67 KB
/
access-note-gen.py
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
#!/usr/bin/env python
# coding=utf8
"""
Convert selected @objc attributes in a source file into access notes, removing
the originals in the process.
"""
from __future__ import print_function
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)
"""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, comment_str):
"""Replace expected-errors with expected-remarks, and make other adjustments
to diagnostics so that they reflect access notes."""
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)),
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, 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."""
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
return u"// access-note-adjust" + offsetify(offset) + u" [attr moved] " + \
u"expected-remark{{access note for fancy tests adds attribute 'objc' to " + \
u"this }} expected-note{{add attribute explicitly to silence this warning}}"
#
# Matching lines
#
"""Matches '@objc(foo) // access-note-move{{access-note-name}}'
or '@objc // access-note-move{{access-note-name}}'"""
access_note_move_re = re.compile(r'@objc(?:\(([\w:]+)\))?[ \t]*' +
r'//[ \t]*access-note-move' +
offset_re_fragment +
r'\{\{([^}]*)\}\}')
"""Matches // access-note-adjust <comment>"""
adjust_comment_re = re.compile(r'//[ \t]*access-note-adjust' + offset_re_fragment +
r'(.*)')
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()