main: Specialize the periodic events on a per-role basis
[tor.git] / scripts / maint / redox.py
blob53d3d902eb87aebf59491b44b0dd38d5eed971fd
1 #!/usr/bin/python
3 # Copyright (c) 2008-2017, The Tor Project, Inc.
4 # See LICENSE for licensing information.
6 # Hi!
7 # I'm redox.py, the Tor redocumentation tool!
8 # I am a horrible hack!
9 # I read the output of doxygen from stderr, and add missing DOCDOC comments
10 # to tell you where documentation should go!
11 # To use me, edit the stuff below...
12 # ...and run 'make doxygen 2>doxygen.stderr' ...
13 # ...and run ./scripts/maint/redox.py < doxygen.stderr !
14 # I'll make a bunch of new files by adding missing DOCDOC comments to your
15 # source. Those files will have names like ./src/common/util.c.newdoc.
16 # You will want to look over the changes by hand before checking them in.
18 # So, here's your workflow:
20 # 0. Make sure you're running a bourne shell for the redirects below.
21 # 1. make doxygen 1>doxygen.stdout 2>doxygen.stderr.
22 # 2. grep Warning doxygen.stderr | grep -v 'is not documented' | less
23 # [This will tell you about all the bogus doxygen output you have]
24 # 3. python ./scripts/maint/redox.py <doxygen.stderr
25 # [This will make lots of .newdoc files with DOCDOC comments for
26 # whatever was missing documentation.]
27 # 4. Look over those .newdoc files, and see which docdoc comments you
28 # want to merge into the main file. If it's all good, just run
29 # "mv fname.c.newdoc fname.c". Otherwise, you'll need to merge
30 # the parts you like by hand.
32 # Which files should we ignore warning from? Mostly, these are external
33 # files that we've snarfed in from somebody else, whose C we do no intend
34 # to document for them.
35 SKIP_FILES = [ "OpenBSD_malloc_Linux.c",
36 "strlcat.c",
37 "strlcpy.c",
38 "sha256.c",
39 "sha256.h",
40 "aes.c",
41 "aes.h" ]
43 # What names of things never need javadoc
44 SKIP_NAME_PATTERNS = [ r'^.*_c_id$',
45 r'^.*_H_ID$' ]
47 # Which types of things should get DOCDOC comments added if they are
48 # missing documentation? Recognized types are in KINDS below.
49 ADD_DOCDOCS_TO_TYPES = [ 'function', 'type', 'typedef' ]
50 ADD_DOCDOCS_TO_TYPES += [ 'variable', ]
52 # ====================
53 # The rest of this should not need hacking.
55 import re
56 import sys
58 KINDS = [ "type", "field", "typedef", "define", "function", "variable",
59 "enumeration" ]
61 NODOC_LINE_RE = re.compile(r'^([^:]+):(\d+): (\w+): (.*) is not documented\.$')
63 THING_RE = re.compile(r'^Member ([a-zA-Z0-9_]+).*\((typedef|define|function|variable|enumeration|macro definition)\) of (file|class) ')
65 SKIP_NAMES = [re.compile(s) for s in SKIP_NAME_PATTERNS]
67 def parsething(thing):
68 """I figure out what 'foobar baz in quux quum is not documented' means,
69 and return: the name of the foobar, and the kind of the foobar.
70 """
71 if thing.startswith("Compound "):
72 tp, name = "type", thing.split()[1]
73 else:
74 m = THING_RE.match(thing)
75 if not m:
76 print thing, "???? Format didn't match."
77 return None, None
78 else:
79 name, tp, parent = m.groups()
80 if parent == 'class':
81 if tp == 'variable' or tp == 'function':
82 tp = 'field'
84 return name, tp
86 def read():
87 """I snarf doxygen stderr from stdin, and parse all the "foo has no
88 documentation messages. I return a map from filename to lists
89 of tuples of (alleged line number, name of thing, kind of thing)
90 """
91 errs = {}
92 for line in sys.stdin:
93 m = NODOC_LINE_RE.match(line)
94 if m:
95 file, line, tp, thing = m.groups()
96 assert tp.lower() == 'warning'
97 name, kind = parsething(thing)
98 errs.setdefault(file, []).append((int(line), name, kind))
100 return errs
102 def findline(lines, lineno, ident):
103 """Given a list of all the lines in the file (adjusted so 1-indexing works),
104 a line number that ident is allegedly on, and ident, I figure out
105 the line where ident was really declared."""
106 lno = lineno
107 for lineno in xrange(lineno, 0, -1):
108 try:
109 if ident in lines[lineno]:
110 return lineno
111 except IndexError:
112 continue
114 return None
116 FUNC_PAT = re.compile(r"^[A-Za-z0-9_]+\(")
118 def hascomment(lines, lineno, kind):
119 """I return true if it looks like there's already a good comment about
120 the thing on lineno of lines of type kind. """
121 if "*/" in lines[lineno-1]:
122 return True
123 if kind == 'function' and FUNC_PAT.match(lines[lineno]):
124 if "*/" in lines[lineno-2]:
125 return True
126 return False
128 def hasdocdoc(lines, lineno, kind):
129 """I return true if it looks like there's already a docdoc comment about
130 the thing on lineno of lines of type kind."""
131 try:
132 if "DOCDOC" in lines[lineno]:
133 return True
134 except IndexError:
135 pass
136 try:
137 if "DOCDOC" in lines[lineno-1]:
138 return True
139 except IndexError:
140 pass
141 if kind == 'function' and FUNC_PAT.match(lines[lineno]):
142 if "DOCDOC" in lines[lineno-2]:
143 return True
144 return False
146 def checkf(fn, errs):
147 """I go through the output of read() for a single file, and build a list
148 of tuples of things that want DOCDOC comments. Each tuple has:
149 the line number where the comment goes; the kind of thing; its name.
151 for skip in SKIP_FILES:
152 if fn.endswith(skip):
153 print "Skipping",fn
154 return
156 comments = []
157 lines = [ None ]
158 try:
159 lines.extend( open(fn, 'r').readlines() )
160 except IOError:
161 return
163 for line, name, kind in errs:
164 if any(pat.match(name) for pat in SKIP_NAMES):
165 continue
167 if kind not in ADD_DOCDOCS_TO_TYPES:
168 continue
170 ln = findline(lines, line, name)
171 if ln == None:
172 print "Couldn't find the definition of %s allegedly on %s of %s"%(
173 name, line, fn)
174 else:
175 if hasdocdoc(lines, line, kind):
176 # print "Has a DOCDOC"
177 # print fn, line, name, kind
178 # print "\t",lines[line-2],
179 # print "\t",lines[line-1],
180 # print "\t",lines[line],
181 # print "-------"
182 pass
183 else:
184 if kind == 'function' and FUNC_PAT.match(lines[ln]):
185 ln = ln - 1
187 comments.append((ln, kind, name))
189 return comments
191 def applyComments(fn, entries):
192 """I apply lots of comments to the file in fn, making a new .newdoc file.
194 N = 0
196 lines = [ None ]
197 try:
198 lines.extend( open(fn, 'r').readlines() )
199 except IOError:
200 return
202 # Process the comments in reverse order by line number, so that
203 # the line numbers for the ones we haven't added yet remain valid
204 # until we add them. Standard trick.
205 entries.sort()
206 entries.reverse()
208 for ln, kind, name in entries:
210 lines.insert(ln, "/* DOCDOC %s */\n"%name)
211 N += 1
213 outf = open(fn+".newdoc", 'w')
214 for line in lines[1:]:
215 outf.write(line)
216 outf.close()
218 print "Added %s DOCDOCs to %s" %(N, fn)
220 e = read()
222 for fn, errs in e.iteritems():
223 print `(fn, errs)`
224 comments = checkf(fn, errs)
225 if comments:
226 applyComments(fn, comments)