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