1+ #!/usr/bin/env python
2+
3+ import os
4+ import re
5+ import json
6+
7+ level_re = re .compile (r'''(Forbid|Deny|Warn|Allow)''' )
8+ conf_re = re .compile (r'''define_Conf! {\n([^}]*)\n}''' , re .MULTILINE )
9+ confvar_re = re .compile (r'''/// Lint: (\w+). (.*).*\n *\("([^"]*)", (?:[^,]*), (.*) => (.*)\),''' )
10+ lint_subheadline = re .compile (r'''^\*\*([\w\s]+)[:?.!]\*\*(.*)''' )
11+
12+ # TODO: actual logging
13+ def warn (* args ): print (args )
14+ def debug (* args ): print (args )
15+ def info (* args ): print (args )
16+
17+ def parse_path (p = "clippy_lints/src" ):
18+ d = []
19+ for f in os .listdir (p ):
20+ if f .endswith (".rs" ):
21+ parse_file (d , os .path .join (p , f ))
22+ return (d , parse_conf (p ))
23+
24+
25+ def parse_conf (p ):
26+ c = {}
27+ with open (p + '/utils/conf.rs' ) as f :
28+ f = f .read ()
29+
30+ m = re .search (conf_re , f )
31+ m = m .groups ()[0 ]
32+
33+ m = re .findall (confvar_re , m )
34+
35+ for (lint , doc , name , default , ty ) in m :
36+ c [lint .lower ()] = (name , ty , doc , default )
37+
38+ return c
39+
40+ def parseLintDef (level , comment , name ):
41+ lint = {}
42+ lint ['id' ] = name
43+ lint ['level' ] = level
44+ lint ['docs' ] = {}
45+
46+ last_section = None
47+
48+ for line in comment :
49+ if len (line .strip ()) == 0 :
50+ continue
51+
52+ match = re .match (lint_subheadline , line )
53+ if match :
54+ last_section = match .groups ()[0 ]
55+ text = match and match .groups ()[1 ] or line
56+
57+ if not last_section :
58+ warn ("Skipping comment line as it was not preceded by a heading" )
59+ debug ("in lint `%s`, line `%s`" % name , line )
60+
61+ lint ['docs' ][last_section ] = (lint ['docs' ].get (last_section , "" ) + "\n " + text ).strip ()
62+
63+ return lint
64+
65+ def parse_file (d , f ):
66+ last_comment = []
67+ comment = True
68+
69+ with open (f ) as rs :
70+ for line in rs :
71+ if comment :
72+ if line .startswith ("///" ):
73+ if line .startswith ("/// " ):
74+ last_comment .append (line [4 :])
75+ else :
76+ last_comment .append (line [3 :])
77+ elif line .startswith ("declare_lint!" ):
78+ comment = False
79+ deprecated = False
80+ restriction = False
81+ elif line .startswith ("declare_restriction_lint!" ):
82+ comment = False
83+ deprecated = False
84+ restriction = True
85+ elif line .startswith ("declare_deprecated_lint!" ):
86+ comment = False
87+ deprecated = True
88+ else :
89+ last_comment = []
90+ if not comment :
91+ l = line .strip ()
92+ m = re .search (r"pub\s+([A-Z_][A-Z_0-9]*)" , l )
93+
94+ if m :
95+ name = m .group (1 ).lower ()
96+
97+ # Intentionally either a never looping or infinite loop
98+ while not deprecated and not restriction :
99+ m = re .search (level_re , line )
100+ if m :
101+ level = m .group (0 )
102+ break
103+
104+ line = next (rs )
105+
106+ if deprecated :
107+ level = "Deprecated"
108+ elif restriction :
109+ level = "Allow"
110+
111+ info ("found %s with level %s in %s" % (name , level , f ))
112+ d .append (parseLintDef (level , last_comment , name = name ))
113+ last_comment = []
114+ comment = True
115+ if "}" in l :
116+ warn ("Warning: Missing Lint-Name in" , f )
117+ comment = True
118+
119+ def main ():
120+ (lints , config ) = parse_path ()
121+ info ("got %s lints" % len (lints ))
122+ with open ("util/gh-pages/lints.json" , "w" ) as file :
123+ json .dump (lints , file , indent = 2 )
124+ info ("wrote JSON for greate justice" )
125+
126+ if __name__ == "__main__" :
127+ main ()
0 commit comments