about summary refs log tree commit diff
path: root/src/etc/errorck.py
blob: 1f5f3784ac61d0c3c92d2f75c83a6349c17187fc (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
# Copyright 2015 The Rust Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution and at
# http://rust-lang.org/COPYRIGHT.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

# Digs error codes out of files named 'diagnostics.rs' across
# the tree, and ensures thare are no duplicates.

import sys
import os
import re

if len(sys.argv) < 2:
    print("usage: errorck.py <src-dir>")
    sys.exit(1)

src_dir = sys.argv[1]
errcode_map = {}
errcode_checked = []
errcode_not_found = []
error_re = re.compile("(E\d\d\d\d)")

def check_unused_error_codes(error_codes, check_error_codes, filenames, dirnames, dirpath):
    for filename in filenames:
        if filename == "diagnostics.rs" or not filename.endswith(".rs"):
            continue
        path = os.path.join(dirpath, filename)

        with open(path, 'r') as f:
            for line in f:
                match = error_re.search(line)
                if match:
                    errcode = match.group(1)
                    if errcode in error_codes:
                        error_codes.remove(errcode)
                    if errcode not in check_error_codes:
                        check_error_codes.append(errcode)
    for dirname in dirnames:
        path = os.path.join(dirpath, dirname)
        for (dirpath, dnames, fnames) in os.walk(path):
            check_unused_error_codes(error_codes, check_error_codes, fnames, dnames, dirpath)


# In the register_long_diagnostics! macro, entries look like this:
#
# EXXXX: r##"
# <Long diagnostic message>
# "##,
#
# These two variables are for detecting the beginning and end of diagnostic
# messages so that duplicate error codes are not reported when a code occurs
# inside a diagnostic message
long_diag_begin = "r##\""
long_diag_end = "\"##"

errors = False
all_errors = []

for (dirpath, dirnames, filenames) in os.walk(src_dir):
    if "src/test" in dirpath or "src/llvm" in dirpath:
        # Short circuit for fast
        continue

    errcode_to_check = []
    for filename in filenames:
        if filename != "diagnostics.rs":
            continue
        path = os.path.join(dirpath, filename)

        with open(path, 'r') as f:
            inside_long_diag = False
            errcode_to_check = []
            for line_num, line in enumerate(f, start=1):
                if inside_long_diag:
                    # Skip duplicate error code checking for this line
                    if long_diag_end in line:
                        inside_long_diag = False
                    continue

                match = error_re.search(line)
                if match:
                    errcode = match.group(1)
                    new_record = [(errcode, path, line_num, line)]
                    existing = errcode_map.get(errcode)
                    if existing is not None:
                        # This is a dupe
                        errcode_map[errcode] = existing + new_record
                    else:
                        errcode_map[errcode] = new_record
                        # we don't check if this is a long error explanation
                        if (long_diag_begin not in line and not line.strip().startswith("//")
                            and errcode not in errcode_to_check and errcode not in errcode_checked
                            and errcode not in errcode_not_found):
                            errcode_to_check.append(errcode)

                if long_diag_begin in line:
                    inside_long_diag = True
        break
    check_unused_error_codes(errcode_to_check, errcode_checked, filenames, dirnames, dirpath)
    if len(errcode_to_check) > 0:
        for errcode in errcode_to_check:
            if errcode in errcode_checked:
                continue
            errcode_not_found.append(errcode)

if len(errcode_not_found) > 0:
    errcode_not_found.sort()
    for errcode in errcode_not_found:
        if errcode in errcode_checked:
            continue
        all_errors.append(errcode)
        print("error: unused error code: {0} ({1}:{2})".format(*errcode_map[errcode][0]))
        errors = True


for errcode, entries in errcode_map.items():
    all_errors.append(entries[0][0])
    if len(entries) > 1:
        entries.sort()
        print("error: duplicate error code " + errcode)
        for entry in entries:
            print("{1}: {2}\n{3}".format(*entry))
        errors = True

print
print("* {0} error codes".format(len(errcode_map)))
print("* highest error code: " + max(all_errors))
print

if errors:
    sys.exit(1)