]> git.lizzy.rs Git - rust.git/blob - util/lintlib.py
Auto merge of #7221 - th1000s:keyword_, r=giraffate
[rust.git] / util / lintlib.py
1 # Common utils for the several housekeeping scripts.
2
3 import os
4 import re
5 import collections
6
7 import logging as log
8 log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s')
9
10 Lint = collections.namedtuple('Lint', 'name level doc sourcefile group')
11 Config = collections.namedtuple('Config', 'name ty doc default')
12
13 lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''')
14 group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
15 conf_re = re.compile(r'''define_Conf! {\n((?!\n})[\s\S])*\n}''', re.MULTILINE)
16 confvar_re = re.compile(
17     r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE)
18 comment_re = re.compile(r'''\s*/// ?(.*)''')
19
20 lint_levels = {
21     "correctness": 'Deny',
22     "suspicious": 'Warn',
23     "style": 'Warn',
24     "complexity": 'Warn',
25     "perf": 'Warn',
26     "restriction": 'Allow',
27     "pedantic": 'Allow',
28     "nursery": 'Allow',
29     "cargo": 'Allow',
30 }
31
32
33 def parse_lints(lints, filepath):
34     comment = []
35     clippy = False
36     deprecated = False
37     name = ""
38
39     with open(filepath) as fp:
40         for line in fp:
41             if clippy or deprecated:
42                 m = lintname_re.search(line)
43                 if m:
44                     name = m.group(1).lower()
45                     line = next(fp)
46
47                     if deprecated:
48                         level = "Deprecated"
49                         group = "deprecated"
50                     else:
51                         while True:
52                             g = group_re.search(line)
53                             if g:
54                                 group = g.group(1).lower()
55                                 level = lint_levels.get(group, None)
56                                 break
57                             line = next(fp)
58
59                     if level is None:
60                         continue
61
62                     log.info("found %s with level %s in %s",
63                              name, level, filepath)
64                     lints.append(Lint(name, level, comment, filepath, group))
65                     comment = []
66
67                     clippy = False
68                     deprecated = False
69                     name = ""
70                 else:
71                     m = comment_re.search(line)
72                     if m:
73                         comment.append(m.group(1))
74             elif line.startswith("declare_clippy_lint!"):
75                 clippy = True
76                 deprecated = False
77             elif line.startswith("declare_deprecated_lint!"):
78                 clippy = False
79                 deprecated = True
80             elif line.startswith("declare_lint!"):
81                 import sys
82                 print(
83                     "don't use `declare_lint!` in Clippy, "
84                     "use `declare_clippy_lint!` instead"
85                 )
86                 sys.exit(42)
87
88
89 def parse_configs(path):
90     configs = {}
91     with open(os.path.join(path, 'utils/conf.rs')) as fp:
92         contents = fp.read()
93
94     match = re.search(conf_re, contents)
95     confvars = re.findall(confvar_re, match.group(0))
96
97     for (lints, doc, name, ty, default) in confvars:
98         for lint in lints.split(','):
99             configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default)
100     return configs
101
102
103 def parse_all(path="clippy_lints/src"):
104     lints = []
105     for root, dirs, files in os.walk(path):
106         for fn in files:
107             if fn.endswith('.rs'):
108                 parse_lints(lints, os.path.join(root, fn))
109
110     log.info("got %s lints", len(lints))
111
112     configs = parse_configs(path)
113     log.info("got %d configs", len(configs))
114
115     return lints, configs