Oops; add missing return to redox.py
[tor/rransom.git] / contrib / redox.py
blobafb457953b0feb5f26defa278922e3fd3882f56b
1 #!/usr/bin/python
3 # Copyright (c) 2008 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 ./contrib/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 ./contrib/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 "eventdns.c",
37 "eventdns.h",
38 "strlcat.c",
39 "strlcpy.c",
40 "aes.c",
41 "aes.h" ]
43 # What names of things never need javadoc
44 SKIP_NAME_PATTERNS = [ r'^.*_c_id$' ]
46 # Which types of things should get DOCDOC comments added if they are
47 # missing documentation? Recognized types are in KINDS below.
48 ADD_DOCDOCS_TO_TYPES = [ 'function', 'type', 'typedef' ]
49 # ADD_DOCDOCS_TO_TYPES += [ 'variable', 'define' ]
51 # ====================
52 # The rest of this should not need hacking.
54 import re
55 import sys
57 KINDS = [ "type", "field", "typedef", "define", "function", "variable" ]
59 NODOC_LINE_RE = re.compile(r'^([^:]+):(\d+): (\w+): (.*) is not documented\.$')
61 THING_RE = re.compile(r'^Member ([a-zA-Z0-9_]+).*\((typedef|define|function|variable)\) of (file|class) ')
63 SKIP_NAMES = [re.compile(s) for s in SKIP_NAME_PATTERNS]
65 def parsething(thing):
66 """I figure out what 'foobar baz in quux quum is not documented' means,
67 and return: the name of the foobar, and the kind of the foobar.
68 """
69 if thing.startswith("Compound "):
70 tp, name = "type", thing.split()[1]
71 else:
72 m = THING_RE.match(thing)
73 if not m:
74 print thing
75 return None, None
76 else:
77 name, tp, parent = m.groups()
78 if parent == 'class':
79 if tp == 'variable' or tp == 'function':
80 tp = 'field'
82 return name, tp
84 def read():
85 """I snarf doxygen stderr from stdin, and parse all the "foo has no
86 documentation messages. I return a map from filename to lists
87 of tuples of (alleged line number, name of thing, kind of thing)
88 """
89 errs = {}
90 for line in sys.stdin:
91 m = NODOC_LINE_RE.match(line)
92 if m:
93 file, line, tp, thing = m.groups()
94 assert tp == 'Warning'
95 name, kind = parsething(thing)
96 errs.setdefault(file, []).append((int(line), name, kind))
98 return errs
100 def findline(lines, lineno, ident):
101 """Given a list of all the lines in the file (adjusted so 1-indexing works),
102 a line number that ident is alledgedly on, and ident, I figure out
103 the line where ident was really declared."""
104 for lineno in xrange(lineno, 0, -1):
105 if ident in lines[lineno]:
106 return lineno
108 return None
110 FUNC_PAT = re.compile(r"^[A-Za-z0-9_]+\(")
112 def hascomment(lines, lineno, kind):
113 """I return true if it looks like there's already a good comment about
114 the thing on lineno of lines of type kind. """
115 if "*/" in lines[lineno-1]:
116 return True
117 if kind == 'function' and FUNC_PAT.match(lines[lineno]):
118 if "*/" in lines[lineno-2]:
119 return True
120 return False
122 def hasdocdoc(lines, lineno, kind):
123 """I return true if it looks like there's already a docdoc comment about
124 the thing on lineno of lines of type kind."""
125 if "DOCDOC" in lines[lineno] or "DOCDOC" in lines[lineno-1]:
126 return True
127 if kind == 'function' and FUNC_PAT.match(lines[lineno]):
128 if "DOCDOC" in lines[lineno-2]:
129 return True
130 return False
132 def checkf(fn, errs):
133 """I go through the output of read() for a single file, and build a list
134 of tuples of things that want DOCDOC comments. Each tuple has:
135 the line number where the comment goes; the kind of thing; its name.
137 for skip in SKIP_FILES:
138 if fn.endswith(skip):
139 print "Skipping",fn
140 return
142 comments = []
143 lines = [ None ]
144 try:
145 lines.extend( open(fn, 'r').readlines() )
146 except IOError:
147 return
149 for line, name, kind in errs:
150 if any(pat.match(name) for pat in SKIP_NAMES):
151 continue
153 if kind not in ADD_DOCDOCS_TO_TYPES:
154 continue
156 ln = findline(lines, line, name)
157 if ln == None:
158 print "Couldn't find the definition of %s allegedly on %s of %s"%(
159 name, line, fn)
160 else:
161 if hasdocdoc(lines, line, kind):
162 # print "Has a DOCDOC"
163 # print fn, line, name, kind
164 # print "\t",lines[line-2],
165 # print "\t",lines[line-1],
166 # print "\t",lines[line],
167 # print "-------"
168 pass
169 else:
170 if kind == 'function' and FUNC_PAT.match(lines[ln]):
171 ln = ln - 1
173 comments.append((ln, kind, name))
175 return comments
177 def applyComments(fn, entries):
178 """I apply lots of comments to the file in fn, making a new .newdoc file.
180 N = 0
182 lines = [ None ]
183 try:
184 lines.extend( open(fn, 'r').readlines() )
185 except IOError:
186 return
188 # Process the comments in reverse order by line number, so that
189 # the line numbers for the ones we haven't added yet remain valid
190 # until we add them. Standard trick.
191 entries.sort()
192 entries.reverse()
194 for ln, kind, name in entries:
196 lines.insert(ln, "/* DOCDOC %s */\n"%name)
197 N += 1
199 outf = open(fn+".newdoc", 'w')
200 for line in lines[1:]:
201 outf.write(line)
202 outf.close()
204 print "Added %s DOCDOCs to %s" %(N, fn)
206 e = read()
208 for fn, errs in e.iteritems():
209 comments = checkf(fn, errs)
210 if comments:
211 applyComments(fn, comments)