From fbb08e0ae5748918fd1eac04ab3234e459165af8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sat, 13 Oct 2018 09:59:02 -0400
Subject: [PATCH 07/18] FIXME: WIP on contrib/apply-fix-it-hints.py

---
 contrib/apply-fix-it-hints.py | 105 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100755 contrib/apply-fix-it-hints.py

diff --git a/contrib/apply-fix-it-hints.py b/contrib/apply-fix-it-hints.py
new file mode 100755
index 0000000..116ed62
--- /dev/null
+++ b/contrib/apply-fix-it-hints.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# FIXME boilerplate
+
+from collections import namedtuple
+import re
+import unittest
+
+"""
+./xgcc -B. -c ../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c -Wall -fdiagnostics-parseable-fixits
+
+e.g.:
+
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:5-145:5}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:9-145:9}:"}}"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:11-145:11}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:15-145:15}:"}}"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:17-145:17}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{145:21-145:21}:"}}"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:5-146:5}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:9-146:9}:"}}"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:11-146:11}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:15-146:15}:"}}"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:17-146:17}:"{{"
+fix-it:"../../src/gcc/testsuite/gcc.dg/Wmissing-braces-fixits.c":{146:23-146:23}:"}}"
+
+"""
+
+class Point(namedtuple('Point', ['line', 'column'])):
+    pass
+
+class Fixit(namedtuple('Fixit', ['filename', 'start', 'end', 'replacement'])):
+    pass
+
+def get_hints_from_stderr(gcc_stderr):
+    hints = []
+    TEXT = '"(.*)"'
+    NUM = "([0-9]+)"
+    POINT = NUM + ':' + NUM
+    pat = ('^fix-it:' + TEXT + ':{' + POINT + '-' + POINT + '}:' + TEXT + '$').encode('ascii')
+    for line in gcc_stderr.splitlines():
+        m = re.match(pat, line)
+        if m:
+            hints.append(Fixit(filename=m.group(1),
+                               start=Point(int(m.group(2)), int(m.group(3))),
+                               end = Point(int(m.group(4)), int(m.group(5))),
+                               replacement=m.group(6)))
+    return hints
+
+class TestParsing(unittest.TestCase):
+    def test_replacements(self):
+        gcc_stderr = """
+../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c: In function ‘test’:
+../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c:12:15: error: ‘struct foo’ has no member named ‘m_bar’; did you mean ‘bar’?
+12 |   return ptr->m_bar; /* { dg-error "'struct foo' has no member named 'm_bar'; did you mean 'bar'?" } */
+   |               ^~~~~
+   |               bar
+fix-it:"../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c":{12:15-12:20}:"bar"
+../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c: In function ‘test6’:
+../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c:52:15: error: ‘union u’ has no member named ‘colour’; did you mean ‘color’?
+52 |   return ptr->colour; /* { dg-error "'union u' has no member named 'colour'; did you mean 'color'?" } */
+   |               ^~~~~~
+   |               color
+fix-it:"../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c":{52:15-52:21}:"color"
+        """.encode('utf-8')
+        hints = get_hints_from_stderr(gcc_stderr)
+        self.assertEqual(len(hints), 2)
+        self.assertEqual(hints[0],
+                         Fixit(filename=b'../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c',
+                               start=Point(line=12, column=15),
+                               end=Point(line=12, column=20),
+                               replacement=b'bar'))
+        self.assertEqual(hints[1],
+                         Fixit(filename=b'../../src/gcc/testsuite/gcc.dg/spellcheck-fields.c',
+                               start=Point(line=52, column=15),
+                               end=Point(line=52, column=21),
+                               replacement=b'color'))
+
+    def test_newline_insertion(self):
+        gcc_stderr = """
+../../src/gcc/testsuite/gcc.dg/missing-header-fixit-1.c:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
++ |+#include <stdio.h>
+1 | /* Example of a fix-it hint that adds a #include directive,
+fix-it:"../../src/gcc/testsuite/gcc.dg/missing-header-fixit-1.c":{1:1-1:1}:"#include <stdio.h>\\n"
+""".encode('utf-8')
+        hints = get_hints_from_stderr(gcc_stderr)
+        self.assertEqual(len(hints), 1)
+        self.assertEqual(hints[0],
+                         Fixit(filename=b'../../src/gcc/testsuite/gcc.dg/missing-header-fixit-1.c',
+                               start=Point(line=1, column=1),
+                               end=Point(line=1, column=1),
+                               replacement=b'#include <stdio.h>\\n'))
+
+def main():
+    unittest.main()
+
+main()
+
+"""
+TODO:
+* conditionalize unittests so it can be run from command line
+* unit test that loads a file, creates modified copy; verify result (perhaps in a tempdir, or via mocking)
+* writing back the result (perhaps to a backup file)
+* use it to write a DejaGnu directive that does a 2nd compile
+* bytes vs unicode
+"""
-- 
1.8.5.3

