comment_splicer_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. # 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. import textwrap
  16. import unittest
  17. from yapf.pytree import comment_splicer
  18. from yapf.pytree import pytree_utils
  19. class CommentSplicerTest(unittest.TestCase):
  20. def _AssertNodeType(self, expected_type, node):
  21. self.assertEqual(expected_type, pytree_utils.NodeName(node))
  22. def _AssertNodeIsComment(self, node, text_in_comment=None):
  23. if pytree_utils.NodeName(node) == 'simple_stmt':
  24. self._AssertNodeType('COMMENT', node.children[0])
  25. node_value = node.children[0].value
  26. else:
  27. self._AssertNodeType('COMMENT', node)
  28. node_value = node.value
  29. if text_in_comment is not None:
  30. self.assertIn(text_in_comment, node_value)
  31. def _FindNthChildNamed(self, node, name, n=1):
  32. for i, child in enumerate(
  33. [c for c in node.pre_order() if pytree_utils.NodeName(c) == name]):
  34. if i == n - 1:
  35. return child
  36. raise RuntimeError('No Nth child for n={0}'.format(n))
  37. def testSimpleInline(self):
  38. code = 'foo = 1 # and a comment\n'
  39. tree = pytree_utils.ParseCodeToTree(code)
  40. comment_splicer.SpliceComments(tree)
  41. expr = tree.children[0].children[0]
  42. # Check that the expected node is still expr_stmt, but now it has 4 children
  43. # (before comment splicing it had 3), the last child being the comment.
  44. self._AssertNodeType('expr_stmt', expr)
  45. self.assertEqual(4, len(expr.children))
  46. comment_node = expr.children[3]
  47. self._AssertNodeIsComment(comment_node, '# and a comment')
  48. def testSimpleSeparateLine(self):
  49. code = textwrap.dedent(r'''
  50. foo = 1
  51. # first comment
  52. bar = 2
  53. ''')
  54. tree = pytree_utils.ParseCodeToTree(code)
  55. comment_splicer.SpliceComments(tree)
  56. # The comment should've been added to the root's children (now 4, including
  57. # the ENDMARKER in the end.
  58. self.assertEqual(4, len(tree.children))
  59. comment_node = tree.children[1]
  60. self._AssertNodeIsComment(comment_node)
  61. def testTwoLineComment(self):
  62. code = textwrap.dedent(r'''
  63. foo = 1
  64. # first comment
  65. # second comment
  66. bar = 2
  67. ''')
  68. tree = pytree_utils.ParseCodeToTree(code)
  69. comment_splicer.SpliceComments(tree)
  70. # This is similar to the single-line standalone comment.
  71. self.assertEqual(4, len(tree.children))
  72. self._AssertNodeIsComment(tree.children[1])
  73. def testCommentIsFirstChildInCompound(self):
  74. code = textwrap.dedent(r'''
  75. if x:
  76. # a comment
  77. foo = 1
  78. ''')
  79. tree = pytree_utils.ParseCodeToTree(code)
  80. comment_splicer.SpliceComments(tree)
  81. # Look into the suite node under the 'if'. We don't care about the NEWLINE
  82. # leaf but the new COMMENT must be a child of the suite and before the
  83. # actual code leaf.
  84. if_suite = tree.children[0].children[3]
  85. self._AssertNodeType('NEWLINE', if_suite.children[0])
  86. self._AssertNodeIsComment(if_suite.children[1])
  87. def testCommentIsLastChildInCompound(self):
  88. code = textwrap.dedent(r'''
  89. if x:
  90. foo = 1
  91. # a comment
  92. ''')
  93. tree = pytree_utils.ParseCodeToTree(code)
  94. comment_splicer.SpliceComments(tree)
  95. # Look into the suite node under the 'if'. We don't care about the DEDENT
  96. # leaf but the new COMMENT must be a child of the suite and after the
  97. # actual code leaf.
  98. if_suite = tree.children[0].children[3]
  99. self._AssertNodeType('DEDENT', if_suite.children[-1])
  100. self._AssertNodeIsComment(if_suite.children[-2])
  101. def testInlineAfterSeparateLine(self):
  102. code = textwrap.dedent(r'''
  103. bar = 1
  104. # line comment
  105. foo = 1 # inline comment
  106. ''')
  107. tree = pytree_utils.ParseCodeToTree(code)
  108. comment_splicer.SpliceComments(tree)
  109. # The separate line comment should become a child of the root, while
  110. # the inline comment remains within its simple_node.
  111. sep_comment_node = tree.children[1]
  112. self._AssertNodeIsComment(sep_comment_node, '# line comment')
  113. expr = tree.children[2].children[0]
  114. inline_comment_node = expr.children[-1]
  115. self._AssertNodeIsComment(inline_comment_node, '# inline comment')
  116. def testSeparateLineAfterInline(self):
  117. code = textwrap.dedent(r'''
  118. bar = 1
  119. foo = 1 # inline comment
  120. # line comment
  121. ''')
  122. tree = pytree_utils.ParseCodeToTree(code)
  123. comment_splicer.SpliceComments(tree)
  124. # The separate line comment should become a child of the root, while
  125. # the inline comment remains within its simple_node.
  126. sep_comment_node = tree.children[-2]
  127. self._AssertNodeIsComment(sep_comment_node, '# line comment')
  128. expr = tree.children[1].children[0]
  129. inline_comment_node = expr.children[-1]
  130. self._AssertNodeIsComment(inline_comment_node, '# inline comment')
  131. def testCommentBeforeDedent(self):
  132. code = textwrap.dedent(r'''
  133. if bar:
  134. z = 1
  135. # a comment
  136. j = 2
  137. ''')
  138. tree = pytree_utils.ParseCodeToTree(code)
  139. comment_splicer.SpliceComments(tree)
  140. # The comment should go under the tree root, not under the 'if'.
  141. self._AssertNodeIsComment(tree.children[1])
  142. if_suite = tree.children[0].children[3]
  143. self._AssertNodeType('DEDENT', if_suite.children[-1])
  144. def testCommentBeforeDedentTwoLevel(self):
  145. code = textwrap.dedent(r'''
  146. if foo:
  147. if bar:
  148. z = 1
  149. # a comment
  150. y = 1
  151. ''')
  152. tree = pytree_utils.ParseCodeToTree(code)
  153. comment_splicer.SpliceComments(tree)
  154. if_suite = tree.children[0].children[3]
  155. # The comment is in the first if_suite, not the nested if under it. It's
  156. # right before the DEDENT
  157. self._AssertNodeIsComment(if_suite.children[-2])
  158. self._AssertNodeType('DEDENT', if_suite.children[-1])
  159. def testCommentBeforeDedentTwoLevelImproperlyIndented(self):
  160. code = textwrap.dedent(r'''
  161. if foo:
  162. if bar:
  163. z = 1
  164. # comment 2
  165. y = 1
  166. ''')
  167. tree = pytree_utils.ParseCodeToTree(code)
  168. comment_splicer.SpliceComments(tree)
  169. # The comment here is indented by 3 spaces, which is unlike any of the
  170. # surrounding statement indentation levels. The splicer attaches it to the
  171. # "closest" parent with smaller indentation.
  172. if_suite = tree.children[0].children[3]
  173. # The comment is in the first if_suite, not the nested if under it. It's
  174. # right before the DEDENT
  175. self._AssertNodeIsComment(if_suite.children[-2])
  176. self._AssertNodeType('DEDENT', if_suite.children[-1])
  177. def testCommentBeforeDedentThreeLevel(self):
  178. code = textwrap.dedent(r'''
  179. if foo:
  180. if bar:
  181. z = 1
  182. # comment 2
  183. # comment 1
  184. # comment 0
  185. j = 2
  186. ''')
  187. tree = pytree_utils.ParseCodeToTree(code)
  188. comment_splicer.SpliceComments(tree)
  189. # comment 0 should go under the tree root
  190. self._AssertNodeIsComment(tree.children[1], '# comment 0')
  191. # comment 1 is in the first if_suite, right before the DEDENT
  192. if_suite_1 = self._FindNthChildNamed(tree, 'suite', n=1)
  193. self._AssertNodeIsComment(if_suite_1.children[-2], '# comment 1')
  194. self._AssertNodeType('DEDENT', if_suite_1.children[-1])
  195. # comment 2 is in if_suite nested under the first if suite,
  196. # right before the DEDENT
  197. if_suite_2 = self._FindNthChildNamed(tree, 'suite', n=2)
  198. self._AssertNodeIsComment(if_suite_2.children[-2], '# comment 2')
  199. self._AssertNodeType('DEDENT', if_suite_2.children[-1])
  200. def testCommentsInClass(self):
  201. code = textwrap.dedent(r'''
  202. class Foo:
  203. """docstring abc..."""
  204. # top-level comment
  205. def foo(): pass
  206. # another comment
  207. ''')
  208. tree = pytree_utils.ParseCodeToTree(code)
  209. comment_splicer.SpliceComments(tree)
  210. class_suite = tree.children[0].children[3]
  211. another_comment = class_suite.children[-2]
  212. self._AssertNodeIsComment(another_comment, '# another')
  213. # It's OK for the comment to be a child of funcdef, as long as it's
  214. # the first child and thus comes before the 'def'.
  215. funcdef = class_suite.children[3]
  216. toplevel_comment = funcdef.children[0]
  217. self._AssertNodeIsComment(toplevel_comment, '# top-level')
  218. def testMultipleBlockComments(self):
  219. code = textwrap.dedent(r'''
  220. # Block comment number 1
  221. # Block comment number 2
  222. def f():
  223. pass
  224. ''')
  225. tree = pytree_utils.ParseCodeToTree(code)
  226. comment_splicer.SpliceComments(tree)
  227. funcdef = tree.children[0]
  228. block_comment_1 = funcdef.children[0]
  229. self._AssertNodeIsComment(block_comment_1, '# Block comment number 1')
  230. block_comment_2 = funcdef.children[1]
  231. self._AssertNodeIsComment(block_comment_2, '# Block comment number 2')
  232. def testCommentsOnDedents(self):
  233. code = textwrap.dedent(r'''
  234. class Foo(object):
  235. # A comment for qux.
  236. def qux(self):
  237. pass
  238. # Interim comment.
  239. def mux(self):
  240. pass
  241. ''')
  242. tree = pytree_utils.ParseCodeToTree(code)
  243. comment_splicer.SpliceComments(tree)
  244. classdef = tree.children[0]
  245. class_suite = classdef.children[6]
  246. qux_comment = class_suite.children[1]
  247. self._AssertNodeIsComment(qux_comment, '# A comment for qux.')
  248. interim_comment = class_suite.children[4]
  249. self._AssertNodeIsComment(interim_comment, '# Interim comment.')
  250. def testExprComments(self):
  251. code = textwrap.dedent(r'''
  252. foo( # Request fractions of an hour.
  253. 948.0/3600, 20)
  254. ''')
  255. tree = pytree_utils.ParseCodeToTree(code)
  256. comment_splicer.SpliceComments(tree)
  257. trailer = self._FindNthChildNamed(tree, 'trailer', 1)
  258. comment = trailer.children[1]
  259. self._AssertNodeIsComment(comment, '# Request fractions of an hour.')
  260. def testMultipleCommentsInOneExpr(self):
  261. code = textwrap.dedent(r'''
  262. foo( # com 1
  263. 948.0/3600, # com 2
  264. 20 + 12 # com 3
  265. )
  266. ''')
  267. tree = pytree_utils.ParseCodeToTree(code)
  268. comment_splicer.SpliceComments(tree)
  269. trailer = self._FindNthChildNamed(tree, 'trailer', 1)
  270. self._AssertNodeIsComment(trailer.children[1], '# com 1')
  271. arglist = self._FindNthChildNamed(tree, 'arglist', 1)
  272. self._AssertNodeIsComment(arglist.children[2], '# com 2')
  273. arith_expr = self._FindNthChildNamed(tree, 'arith_expr', 1)
  274. self._AssertNodeIsComment(arith_expr.children[-1], '# com 3')
  275. if __name__ == '__main__':
  276. unittest.main()