Macroscope can now handle multiple definitions of macros gracefully,
as long as all but one is local (e.g. scope is terminated by an #undef)
This commit is contained in:
parent
cfabaaf134
commit
234ddfe2d9
1 changed files with 67 additions and 34 deletions
|
@ -96,15 +96,26 @@ def iswml(filename):
|
|||
|
||||
class reference:
|
||||
"Describes a location by file and line."
|
||||
def __init__(self, filename, line=None, docstring=None):
|
||||
def __init__(self, filename, lineno=None, docstring=None):
|
||||
self.filename = filename
|
||||
self.line = line
|
||||
self.lineno = lineno
|
||||
self.references = {}
|
||||
self.docstring = docstring
|
||||
self.undef = None
|
||||
def append(self, fn, n):
|
||||
if fn not in self.references:
|
||||
self.references[fn] = []
|
||||
self.references[fn].append(n+1)
|
||||
def visible_from(self, fn, n):
|
||||
"Is this definition visible from the specified file and line?"
|
||||
if self.undef != None:
|
||||
return self.filename == fn
|
||||
else:
|
||||
# Sigh. This doesn't match the actual preprocessor semantics.
|
||||
# It means any macro without an undef is visible anywhere.
|
||||
# We can't do better than this without a lot of hairy graph-
|
||||
# coloring logic to simulate include path interpretation.
|
||||
return True
|
||||
def dump_references(self):
|
||||
for (file, linenumbers) in self.references.items():
|
||||
print " %s: %s" % (file, `linenumbers`[1:-1])
|
||||
|
@ -117,10 +128,10 @@ class reference:
|
|||
if byfile:
|
||||
return byfile
|
||||
else:
|
||||
return cmp(self.line, other.line)
|
||||
return cmp(self.lineno, other.lineno)
|
||||
def __str__(self):
|
||||
if self.line:
|
||||
return '"%s", line %d' % (self.filename, self.line)
|
||||
if self.lineno:
|
||||
return '"%s", line %d' % (self.filename, self.lineno)
|
||||
else:
|
||||
return self.filename
|
||||
|
||||
|
@ -207,16 +218,20 @@ class CrossRef:
|
|||
here.hash.update(line)
|
||||
here.hash = here.hash.digest()
|
||||
if name in self.xref:
|
||||
if self.xref[name].hash != here.hash:
|
||||
print >>sys.stderr, "*** Warning: different " \
|
||||
"definition of %s from %s, at %s" \
|
||||
% (name, self.xref[name], here)
|
||||
elif warnlevel > 0:
|
||||
print >>sys.stderr, "*** Warning: duplicate " \
|
||||
"definition of %s from %s, at %s" \
|
||||
% (name, self.xref[name], here)
|
||||
else:
|
||||
self.xref[name] = here
|
||||
for defn in self.xref[name]:
|
||||
if not defn.visible_from(filename, n):
|
||||
continue
|
||||
elif defn.hash != here.hash:
|
||||
print >>sys.stderr, \
|
||||
"%s: overrides different %s definition at %s" \
|
||||
% (here, name, defn)
|
||||
elif warnlevel > 0:
|
||||
print >>sys.stderr, \
|
||||
"%s: duplicates %s definition at %s" \
|
||||
% (here, name, defn)
|
||||
if name not in self.xref:
|
||||
self.xref[name] = []
|
||||
self.xref[name].append(here)
|
||||
state = "outside"
|
||||
elif state == "macro_header" and line and line[0] != "#":
|
||||
state = "macro_body"
|
||||
|
@ -224,6 +239,11 @@ class CrossRef:
|
|||
here.docstring += line[1:]
|
||||
if state in ("macro_header", "macro_body"):
|
||||
here.hash.update(line)
|
||||
elif line.strip().startswith("#undef"):
|
||||
tokens = line.split()
|
||||
name = tokens[1]
|
||||
# Potential bug here on unbalanced #undef
|
||||
self.xref[name][-1].undef = n
|
||||
dfp.close()
|
||||
elif filename.endswith(".def"):
|
||||
# It's a list of names to be considered defined
|
||||
|
@ -252,11 +272,17 @@ class CrossRef:
|
|||
# Find references to macros
|
||||
for match in re.finditer(CrossRef.macro_reference, line):
|
||||
name = match.group(1)
|
||||
candidates = 0
|
||||
if name in formals:
|
||||
continue
|
||||
elif name in self.xref:
|
||||
self.xref[name].append(fn, n+1)
|
||||
else:
|
||||
for defn in self.xref[name]:
|
||||
if defn.visible_from(fn, n):
|
||||
candidates += 1
|
||||
defn.append(fn, n+1)
|
||||
if candidates > 1:
|
||||
print "%s: more than one definition of %s is visible here." % (reference(fn, n), name)
|
||||
if candidates == 0:
|
||||
self.unresolved.append((name, reference(fn,n+1)))
|
||||
# Find references to resource files
|
||||
for match in re.finditer(CrossRef.file_reference, line):
|
||||
|
@ -292,15 +318,20 @@ class CrossRef:
|
|||
rfp.close()
|
||||
def xrefdump(self, pred=None):
|
||||
"Report resolved macro references."
|
||||
for (name, defloc) in self.xref.items():
|
||||
if pred and not pred(name, defloc):
|
||||
continue
|
||||
nrefs = len(defloc.references)
|
||||
if nrefs == 0:
|
||||
print "%s: macro %s is unused" % (defloc, name)
|
||||
else:
|
||||
print "%s: macro %s is used in %d files:" % (defloc, name, nrefs)
|
||||
defloc.dump_references()
|
||||
for name in self.xref:
|
||||
for defn in self.xref[name]:
|
||||
if pred and not pred(name, defn):
|
||||
continue
|
||||
defn.dump_references()
|
||||
if defn.undef:
|
||||
type = "local"
|
||||
else:
|
||||
type = "global"
|
||||
nrefs = len(defn.references)
|
||||
if nrefs == 0:
|
||||
print "%s: %s macro %s is unused" % (defn, type, name)
|
||||
else:
|
||||
print "%s: %s macro %s is used in %d files:" % (defn, type, name, nrefs)
|
||||
for (name, defloc) in self.fileref.items():
|
||||
if pred and not pred(name, defloc):
|
||||
continue
|
||||
|
@ -321,22 +352,24 @@ class CrossRef:
|
|||
print "%s -> %s" % (reference, name)
|
||||
def deflist(self, pred=None):
|
||||
"List all resource definitions."
|
||||
for (name, defloc) in self.xref.items() + self.fileref.items():
|
||||
if pred and not pred(name, defloc):
|
||||
continue
|
||||
nrefs = len(defloc.references)
|
||||
if nrefs:
|
||||
for name in self.xref:
|
||||
for defn in self.xref[name]:
|
||||
if not pred or pred(name, defn):
|
||||
print name
|
||||
for (name, defloc) in self.fileref.items():
|
||||
if not pred or pred(name, defloc):
|
||||
print name
|
||||
def extracthelp(self, pref, fp):
|
||||
"Deliver all macro help comments in HTML form."
|
||||
# Bug: finds only the first definition of each macro in scope.
|
||||
doclist = self.xref.keys()
|
||||
doclist = filter(lambda x: self.xref[x].docstring.count("\n") > 1, doclist)
|
||||
doclist.sort(lambda x, y: cmp(self.xref[x], self.xref[y]))
|
||||
doclist = filter(lambda x: self.xref[x][0].docstring.count("\n") > 1, doclist)
|
||||
doclist.sort(lambda x, y: cmp(self.xref[x][0], self.xref[y]))
|
||||
outstr = ""
|
||||
filename = None
|
||||
counted = 0
|
||||
for name in doclist:
|
||||
entry = self.xref[name]
|
||||
entry = self.xref[name][0]
|
||||
if entry.filename != filename:
|
||||
if counted:
|
||||
outstr += "</dl>\n"
|
||||
|
|
Loading…
Add table
Reference in a new issue