github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/scripts/find-bad-doc-comments.py (about)

     1  #!/usr/bin/python
     2  
     3  # Copyright 2014 Canonical Ltd.
     4  # Licensed under the AGPLv3, see LICENCE file for details.
     5  
     6  """
     7  This quick-and-dirty tool locates Go doc comments in a source tree
     8  that don't follow the convention of the first word of the comment
     9  matching the function name. It highlights cases where doc comments
    10  haven't been updated in step with function name changes or where doc
    11  comments have been copied and pasted but not updated.
    12  
    13  Tests are excluded by the check, unless --tests is given.
    14  Unexported methods and functions are also excluded, unless --unexported
    15  is given.
    16  
    17  By default, all problems found are emitted but there is also an
    18  interactive edit mode is available via --fix.
    19  """
    20  
    21  import argparse
    22  import fnmatch
    23  import os
    24  import re
    25  import subprocess
    26  from os import path
    27  
    28  def find_go_files(root):
    29      for directory, _, files in os.walk(root):
    30          for filename in fnmatch.filter(files, '*.go'):
    31              yield path.join(directory, filename)
    32  
    33  DOC_COMMENT_PATT = '\n\n//.+\n(//.+\n)*func.+\n'
    34  FIRST_WORD_PATT = '// *(\w+)'
    35  FUNC_NAME_PATT = 'func(?: \([^)]+\))? (\S+)\('
    36  
    37  def extract_doc_comments(text):
    38      for match in re.finditer(DOC_COMMENT_PATT, text, re.MULTILINE):
    39          yield match.group(0).strip()
    40  
    41  def find_bad_doc_comments(comments):
    42      for comment in comments:
    43          lines = comment.splitlines()
    44          first_word_match = re.match(FIRST_WORD_PATT, lines[0])
    45          if first_word_match:
    46              first_word = first_word_match.group(1)
    47              func_name = re.match(FUNC_NAME_PATT, lines[-1]).group(1)
    48              if first_word != func_name:
    49                  yield func_name, comment
    50  
    51  def cmdline():
    52      parser = argparse.ArgumentParser(description=__doc__)
    53      parser.add_argument('--fix', default=False, action='store_true',
    54                          help='Interactive fix-up mode')
    55      parser.add_argument('--tests', default=False, action='store_true',
    56                          help='Include test methods in the check')
    57      parser.add_argument('--unexported', default=False, action='store_true',
    58                          help='Include unexported methods in the check')
    59      parser.add_argument('root', nargs='?', default=os.getcwd())
    60      return parser.parse_args()
    61  
    62  def emit(filename, comment):
    63      print
    64      print '%s: ' % filename
    65      print comment
    66  
    67  def fix(filename, func_name, comment):
    68      emit(filename, comment)
    69      resp = raw_input('Fix? [Y/n] ').strip().lower()
    70      if resp in ('', 'y'):
    71          subprocess.check_call(['vim', '-c', '/func .*'+func_name+'(', filename])
    72  
    73  def main():
    74      args = cmdline()
    75  
    76      count = 0
    77      for filename in find_go_files(args.root):
    78          with open(filename) as sourceFile:
    79              source = sourceFile.read()
    80          comments = extract_doc_comments(source)
    81          for func_name, bad_comment in find_bad_doc_comments(comments):
    82              if func_name.startswith('Test') and not args.tests:
    83                  # Skip tests unless told otherwise.
    84                  continue
    85              if 'export_test.go' in filename and not args.tests:
    86                  # Skip export_test.go unless --tests is given.
    87                  continue
    88              if func_name[0].islower() and not args.unexported:
    89                  # Skip unexported unless told otherwise.
    90                  continue
    91              if args.fix:
    92                  fix(filename, func_name, bad_comment)
    93              else:
    94                  emit(filename, bad_comment)
    95              count += 1
    96  
    97      print
    98      print "Problems found:", count
    99  
   100  if __name__ == '__main__':
   101      main()