/tools/clang/scripts/apply_fixits.py

https://github.com/chromium/chromium · Python · 87 lines · 51 code · 9 blank · 27 comment · 9 complexity · d5c1707cf11b37285c17b051e6cb34f6 MD5 · raw file

  1. #!/usr/bin/env python
  2. # Copyright 2015 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. #
  6. # Script to apply fixits generated by clang. This is to work around the fact
  7. # that clang's -Xclang -fixit-recompile flag, which automatically applies fixits
  8. # and recompiles, doesn't work well with parallel invocations of clang.
  9. #
  10. # Usage:
  11. # 1. Enable parseable fixits and disable warnings as errors. Instructions for
  12. # doing this vary based on the build environment, but for GN, warnings as
  13. # errors can be disabled by setting treat_warnings_as_errors = false
  14. # Enabling parseable fixits requires editing build/config/compiler/BUILD.gn
  15. # and adding `-fdiagnostics-parseable-fixits` to cflags.
  16. # 2. Build everything and capture the output:
  17. # ninja -C <build_directory> &> generated-fixits
  18. # 3. Apply the fixits with this script:
  19. # python apply_fixits.py -p <build_directory> < generated-fixits
  20. from __future__ import print_function
  21. import argparse
  22. import collections
  23. import fileinput
  24. import os
  25. import re
  26. import sys
  27. # fix-it:"../../base/threading/sequenced_worker_pool.h":{341:3-341:11}:""
  28. # Note that the file path is relative to the build directory.
  29. _FIXIT_RE = re.compile(r'^fix-it:"(?P<file>.+?)":'
  30. r'{(?P<start_line>\d+?):(?P<start_col>\d+?)-'
  31. r'(?P<end_line>\d+?):(?P<end_col>\d+?)}:'
  32. r'"(?P<text>.*?)"$')
  33. FixIt = collections.namedtuple(
  34. 'FixIt', ('start_line', 'start_col', 'end_line', 'end_col', 'text'))
  35. def main():
  36. parser = argparse.ArgumentParser()
  37. parser.add_argument(
  38. '-p',
  39. required=True,
  40. help='path to the build directory to complete relative paths in fixits')
  41. args = parser.parse_args()
  42. fixits = collections.defaultdict(list)
  43. for line in fileinput.input(['-']):
  44. if not line.startswith('fix-it:'):
  45. continue
  46. m = _FIXIT_RE.match(line)
  47. if not m:
  48. continue
  49. # The negative line numbers are a cheap hack so we can sort things in line
  50. # order but reverse column order. Applying the fixits in reverse order makes
  51. # things simpler, since offsets won't have to be adjusted as the text is
  52. # changed.
  53. fixits[m.group('file')].append(FixIt(
  54. int(m.group('start_line')), -int(m.group('start_col')), int(m.group(
  55. 'end_line')), -int(m.group('end_col')), m.group('text')))
  56. for k, v in fixits.iteritems():
  57. v.sort()
  58. with open(os.path.join(args.p, k), 'rb+') as f:
  59. lines = f.readlines()
  60. last_fixit = None
  61. for fixit in v:
  62. if fixit.start_line != fixit.end_line:
  63. print('error: multiline fixits not supported! file: %s, fixit: %s' %
  64. (k, fixit))
  65. sys.exit(1)
  66. if fixit == last_fixit:
  67. continue
  68. last_fixit = fixit
  69. # The line/column numbers emitted in fixit hints start at 1, so offset
  70. # is appropriately.
  71. line = lines[fixit.start_line - 1]
  72. lines[fixit.start_line - 1] = (line[:-fixit.start_col - 1] + fixit.text
  73. + line[-fixit.end_col - 1:])
  74. f.seek(0)
  75. f.truncate()
  76. f.writelines(lines)
  77. if __name__ == '__main__':
  78. sys.exit(main())