summaryrefslogtreecommitdiff
path: root/massage_messages.py
blob: 34d18eaffe0eb9be01ad0fa9b45a8f25dead3013 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from collections import defaultdict

import fileinput
import re
import subprocess
import sys


"""

source/organizer/aboutdialog.h should add these lines:
#include <QObject>  // for Q_OBJECT, slots
#include <QString>  // for QString
class QListWidgetItem;
class QWidget;

source/organizer/aboutdialog.h should remove these lines:
- #include <QListWidgetItem>  // lines 25-25
- #include <utility>  // lines 28-28
- #include <vector>  // lines 27-27
- class DownloadManager;  // lines 47-47

The full include-list for source/organizer/aboutdialog.h:
#include <QDialog>  // for QDialog
#include <QObject>  // for Q_OBJECT, slots
#include <QString>  // for QString
#include <map>      // for map
class QListWidgetItem;
class QWidget;
namespace Ui { class AboutDialog; }  // lines 31-31
---
"""

removing = None
includes = dict()

adding = None
added = defaultdict(list)
lcadded = dict()

foundline = None

errors = False

messages = defaultdict(list)

def process_next_line(line, outfile):
    """ Read a line of output/error from include-what-you use
        Turn clang errors into a form QT creator recognises
        Raise warnings for unneeded includes
    """
    global removing
    global includes
    global foundline
    global errors
    global added
    global adding
    line = line.rstrip()
    print >> outfile, line

    if line.endswith(' should remove these lines:'):
        adding = None
        removing = (line.split(' '))[0]
    elif line.endswith(' should add these lines:'):
        removing = None
        adding = (line.split(' '))[0]
    elif line == '' or line.startswith(' the full include-list'):
        adding = None
        removing = None
    elif adding:
        m = re.match(r'.*class (.*);', line)
        if m:
            added[m.group(1)].append((adding, line))
            lcadded[m.group(1).lower() + '.h'] = m.group(1)
        else:
            added[line].append((adding, line))
    elif removing:
        # Really we should stash these so that if we get a 'class xxx' in
        # the add lines we can print it here. also we could do the case
        # fixing.
        m = re.match(r'- #include [<"](.*)[">] +// lines (.*)-', line)
        if m:
            foundline = m.group(2)
            # Note: In this project at least we have a naming convention of
            # lower case filename and upper case classname.
            clname = m.group(1)
            if clname not in added:
                if clname in lcadded:
                    clname = lcadded[clname]
            if clname in added:
                for item in added[clname]:
                    if removing == item[0]:
                        messages[removing].append(
                            '%s(%s) : warning I0004: Replace include of %s'
                            ' with forward reference %s' % (
                                     removing, m.group(2), m.group(1), item[1]))
                del added[clname]
            else:
                messages[removing].append(
                    '%s(%s) : warning I0001: Unnecessary include of %s' % (
                                              removing, m.group(2), m.group(1)))

        else:
            m = re.match(r'- (.*) +// lines (.*)-', line)
            if m:
                messages[removing].append(
                    '%s(%s) : warning I0002: Unnecessary forward ref of %s' % (
                                              removing, m.group(2), m.group(1)))
            else:
                print '********* I got confused **********'
    elif line.startswith('In file included from'):
        line = re.sub(r'^(In file included from)(.*):(\d+):',
                      r'        \2(\3) : \1 here',
                      line)
        # Note; QT Creator seems to be unwilling to let you double click the
        # line to select the code in question if you get a string of these, not
        # sure why.
    elif ': note:' in line:
        line = '        ' + re.sub(r':(\d+):\d+: note:', r'(\1) : note:', line)
    else:
        # Replace clang :line:column: type: with ms (line) : type nnnn:
        line = re.sub(r':(\d+):\d+: ([^:]*):', r'(\1) : \2 I1234:', line)
        if ' : error I1234:' in line:
            errors = True

    print line

outfile = open(sys.argv[1], 'w')
process = subprocess.Popen(sys.argv[2:],
                           stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
while True:
    output = process.stdout.readline()
    if output == '' and process.poll() is not None:
        break
    if output:
        process_next_line(output, outfile)

# A note: We should probably do some work as we use the source code line for
# messages in include files...

if foundline is None:
    foundline = '1'

for add in added:
    for item in added[add]:
        messages[item[0]].append(
            '%s(%s) : warning I0003: Need to include %s' % (
                                                   item[0], foundline, item[1]))

for file in sorted(messages.keys(), reverse = True):
    for line in messages[file]:
        print line

rc = process.poll()
# The return code you get appears to be more to do with the amount of output
# generated than any real error, so instead we should error if any ': error:'
# lines are detected

if errors:
    sys.exit(1)