blob: 8a6718052ea943680281bff524ac1cf0d2286c46 [file] [log] [blame]
Bill Wendling7d623452015-03-18 13:36:07 -07001# Copyright 2015 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tests for yapf.comment_splicer."""
15
Bill Wendling7d623452015-03-18 13:36:07 -070016import textwrap
17import unittest
18
19from yapf.yapflib import comment_splicer
Eli Benderskyaaaa4642015-03-19 16:11:04 -070020from yapf.yapflib import py3compat
Bill Wendling7d623452015-03-18 13:36:07 -070021from yapf.yapflib import pytree_utils
22
23
24class CommentSplicerTest(unittest.TestCase):
25
26 def _AssertNodeType(self, expected_type, node):
27 self.assertEqual(expected_type, pytree_utils.NodeName(node))
28
29 def _AssertNodeIsComment(self, node, text_in_comment=None):
30 if pytree_utils.NodeName(node) == 'simple_stmt':
31 self._AssertNodeType('COMMENT', node.children[0])
32 node_value = node.children[0].value
33 else:
34 self._AssertNodeType('COMMENT', node)
35 node_value = node.value
36 if text_in_comment is not None:
37 self.assertIn(text_in_comment, node_value)
38
39 def _FindNthChildNamed(self, node, name, n=1):
Eli Benderskyaaaa4642015-03-19 16:11:04 -070040 for i, child in enumerate(py3compat.ifilter(
Bill Wendling7d623452015-03-18 13:36:07 -070041 lambda c: pytree_utils.NodeName(c) == name, node.pre_order())):
42 if i == n - 1:
43 return child
44 raise RuntimeError('No Nth child for n={0}'.format(n))
45
46 def testSimpleInline(self):
47 code = 'foo = 1 # and a comment\n'
48 tree = pytree_utils.ParseCodeToTree(code)
49 comment_splicer.SpliceComments(tree)
50
51 expr = tree.children[0].children[0]
52 # Check that the expected node is still expr_stmt, but now it has 4 children
53 # (before comment splicing it had 3), the last child being the comment.
54 self._AssertNodeType('expr_stmt', expr)
55 self.assertEqual(4, len(expr.children))
56 comment_node = expr.children[3]
57 self._AssertNodeIsComment(comment_node, '# and a comment')
58
59 def testSimpleSeparateLine(self):
60 code = textwrap.dedent(r'''
61 foo = 1
62 # first comment
63 bar = 2
64 ''')
65 tree = pytree_utils.ParseCodeToTree(code)
66 comment_splicer.SpliceComments(tree)
67
68 # The comment should've been added to the root's children (now 4, including
69 # the ENDMARKER in the end.
70 self.assertEqual(4, len(tree.children))
71 comment_node = tree.children[1]
72 self._AssertNodeIsComment(comment_node)
73
74 def testTwoLineComment(self):
75 code = textwrap.dedent(r'''
76 foo = 1
77 # first comment
78 # second comment
79 bar = 2
80 ''')
81 tree = pytree_utils.ParseCodeToTree(code)
82 comment_splicer.SpliceComments(tree)
83
84 # This is similar to the single-line standalone comment.
85 self.assertEqual(4, len(tree.children))
86 self._AssertNodeIsComment(tree.children[1])
87
88 def testCommentIsFirstChildInCompound(self):
89 code = textwrap.dedent(r'''
90 if x:
91 # a comment
92 foo = 1
93 ''')
94 tree = pytree_utils.ParseCodeToTree(code)
95 comment_splicer.SpliceComments(tree)
96
97 # Look into the suite node under the 'if'. We don't care about the NEWLINE
98 # leaf but the new COMMENT must be a child of the suite and before the
99 # actual code leaf.
100 if_suite = tree.children[0].children[3]
101 self._AssertNodeType('NEWLINE', if_suite.children[0])
102 self._AssertNodeIsComment(if_suite.children[1])
103
104 def testCommentIsLastChildInCompound(self):
105 code = textwrap.dedent(r'''
106 if x:
107 foo = 1
108 # a comment
109 ''')
110 tree = pytree_utils.ParseCodeToTree(code)
111 comment_splicer.SpliceComments(tree)
112
113 # Look into the suite node under the 'if'. We don't care about the DEDENT
114 # leaf but the new COMMENT must be a child of the suite and after the
115 # actual code leaf.
116 if_suite = tree.children[0].children[3]
117 self._AssertNodeType('DEDENT', if_suite.children[-1])
118 self._AssertNodeIsComment(if_suite.children[-2])
119
120 def testInlineAfterSeparateLine(self):
121 code = textwrap.dedent(r'''
122 bar = 1
123 # line comment
124 foo = 1 # inline comment
125 ''')
126 tree = pytree_utils.ParseCodeToTree(code)
127 comment_splicer.SpliceComments(tree)
128
129 # The separate line comment should become a child of the root, while
130 # the inline comment remains within its simple_node.
131 sep_comment_node = tree.children[1]
132 self._AssertNodeIsComment(sep_comment_node, '# line comment')
133
134 expr = tree.children[2].children[0]
135 inline_comment_node = expr.children[-1]
136 self._AssertNodeIsComment(inline_comment_node, '# inline comment')
137
138 def testSeparateLineAfterInline(self):
139 code = textwrap.dedent(r'''
140 bar = 1
141 foo = 1 # inline comment
142 # line comment
143 ''')
144 tree = pytree_utils.ParseCodeToTree(code)
145 comment_splicer.SpliceComments(tree)
146
147 # The separate line comment should become a child of the root, while
148 # the inline comment remains within its simple_node.
149 sep_comment_node = tree.children[-2]
150 self._AssertNodeIsComment(sep_comment_node, '# line comment')
151
152 expr = tree.children[1].children[0]
153 inline_comment_node = expr.children[-1]
154 self._AssertNodeIsComment(inline_comment_node, '# inline comment')
155
156 def testCommentBeforeDedent(self):
157 code = textwrap.dedent(r'''
158 if bar:
159 z = 1
160 # a comment
161 j = 2
162 ''')
163 tree = pytree_utils.ParseCodeToTree(code)
164 comment_splicer.SpliceComments(tree)
165
166 # The comment should go under the tree root, not under the 'if'.
167 self._AssertNodeIsComment(tree.children[1])
168 if_suite = tree.children[0].children[3]
169 self._AssertNodeType('DEDENT', if_suite.children[-1])
170
171 def testCommentBeforeDedentTwoLevel(self):
172 code = textwrap.dedent(r'''
173 if foo:
174 if bar:
175 z = 1
176 # a comment
177 y = 1
178 ''')
179 tree = pytree_utils.ParseCodeToTree(code)
180 comment_splicer.SpliceComments(tree)
181
182 if_suite = tree.children[0].children[3]
183 # The comment is in the first if_suite, not the nested if under it. It's
184 # right before the DEDENT
185 self._AssertNodeIsComment(if_suite.children[-2])
186 self._AssertNodeType('DEDENT', if_suite.children[-1])
187
188 def testCommentBeforeDedentTwoLevelImproperlyIndented(self):
189 code = textwrap.dedent(r'''
190 if foo:
191 if bar:
192 z = 1
193 # comment 2
194 y = 1
195 ''')
196 tree = pytree_utils.ParseCodeToTree(code)
197 comment_splicer.SpliceComments(tree)
198
199 # The comment here is indented by 3 spaces, which is unlike any of the
200 # surrounding statement indentation levels. The splicer attaches it to the
201 # "closest" parent with smaller indentation.
202 if_suite = tree.children[0].children[3]
203 # The comment is in the first if_suite, not the nested if under it. It's
204 # right before the DEDENT
205 self._AssertNodeIsComment(if_suite.children[-2])
206 self._AssertNodeType('DEDENT', if_suite.children[-1])
207
208 def testCommentsInClass(self):
209 code = textwrap.dedent(r'''
210 class Foo:
211 """docstring abc..."""
212 # top-level comment
213 def foo(): pass
214 # another comment
215 ''')
216
217 tree = pytree_utils.ParseCodeToTree(code)
218 comment_splicer.SpliceComments(tree)
219
220 class_suite = tree.children[0].children[3]
221 another_comment = class_suite.children[-2]
222 self._AssertNodeIsComment(another_comment, '# another')
223
224 # It's OK for the comment to be a child of funcdef, as long as it's
225 # the first child and thus comes before the 'def'.
226 funcdef = class_suite.children[3]
227 toplevel_comment = funcdef.children[0]
228 self._AssertNodeIsComment(toplevel_comment, '# top-level')
229
230 def testMultipleBlockComments(self):
231 code = textwrap.dedent(r'''
232 # Block comment number 1
233
234 # Block comment number 2
235 def f():
236 pass
237 ''')
238
239 tree = pytree_utils.ParseCodeToTree(code)
240 comment_splicer.SpliceComments(tree)
241
242 funcdef = tree.children[0]
243 block_comment_1 = funcdef.children[0]
244 self._AssertNodeIsComment(block_comment_1, '# Block comment number 1')
245
246 block_comment_2 = funcdef.children[1]
247 self._AssertNodeIsComment(block_comment_2, '# Block comment number 2')
248
249 def testCommentsOnDedents(self):
250 code = textwrap.dedent(r'''
251 class Foo(object):
252 # A comment for qux.
253 def qux(self):
254 pass
255
256 # Interim comment.
257
258 def mux(self):
259 pass
260 ''')
261
262 tree = pytree_utils.ParseCodeToTree(code)
263 comment_splicer.SpliceComments(tree)
264
265 classdef = tree.children[0]
266 class_suite = classdef.children[6]
267 qux_comment = class_suite.children[1]
268 self._AssertNodeIsComment(qux_comment, '# A comment for qux.')
269
270 interim_comment = class_suite.children[4]
271 self._AssertNodeIsComment(interim_comment, '# Interim comment.')
272
273 def testExprComments(self):
274 code = textwrap.dedent(r'''
275 foo( # Request fractions of an hour.
276 948.0/3600, 20)
277 ''')
278 tree = pytree_utils.ParseCodeToTree(code)
279 comment_splicer.SpliceComments(tree)
280
281 trailer = self._FindNthChildNamed(tree, 'trailer', 1)
282 comment = trailer.children[1]
283 self._AssertNodeIsComment(comment, '# Request fractions of an hour.')
284
285 def testMultipleCommentsInOneExpr(self):
286 code = textwrap.dedent(r'''
287 foo( # com 1
288 948.0/3600, # com 2
289 20 + 12 # com 3
290 )
291 ''')
292 tree = pytree_utils.ParseCodeToTree(code)
293 comment_splicer.SpliceComments(tree)
294
295 trailer = self._FindNthChildNamed(tree, 'trailer', 1)
296 self._AssertNodeIsComment(trailer.children[1], '# com 1')
297
298 arglist = self._FindNthChildNamed(tree, 'arglist', 1)
299 self._AssertNodeIsComment(arglist.children[2], '# com 2')
300
301 arith_expr = self._FindNthChildNamed(tree, 'arith_expr', 1)
302 self._AssertNodeIsComment(arith_expr.children[-1], '# com 3')
303
304
Bill Wendling7d623452015-03-18 13:36:07 -0700305if __name__ == '__main__':
306 unittest.main()