1#!/usr/local/bin/python3.8
2
3import sys
4import re
5import io
6
7callDict = dict() # callInfo tuple -> callValue
8definitionToSourceLocationMap = dict()
9paramSet = set() # paraminfo tuple
10
11# clang does not always use exactly the same numbers in the type-parameter vars it generates
12# so I need to substitute them to ensure we can match correctly.
13normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
14def normalizeTypeParams( line ):
15    return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
16
17# reading as binary (since we known it is pure ascii) is much faster than reading as unicode
18with io.open("workdir/loplugin.virtualdead.log", "r", encoding="ascii", errors="ignore", buffering=1024*1024) as txt:
19    for line in txt:
20        try:
21            tokens = line.strip().split("\t")
22            if tokens[0] == "virtual:":
23                nameAndParams = normalizeTypeParams(tokens[1])
24                sourceLocation = tokens[2]
25                returnValue = tokens[3]
26                callInfo = (nameAndParams, sourceLocation)
27                if not callInfo in callDict:
28                    callDict[callInfo] = set()
29                callDict[callInfo].add(returnValue)
30                definitionToSourceLocationMap[nameAndParams] = sourceLocation
31            elif tokens[0] == "param:":
32                name = normalizeTypeParams(tokens[1])
33                if len(tokens)>2:
34                    bitfield = tokens[2]
35                    paramSet.add((name,bitfield))
36            else:
37                print( "unknown line: " + line)
38        except IndexError:
39            print("problem with line " + line.strip())
40            raise
41
42tmp1list = list()
43for callInfo, callValues in iter(callDict.items()):
44    nameAndParams = callInfo[1]
45    if len(callValues) != 1:
46        continue
47    callValue = next(iter(callValues))
48    if "unknown-stmt" in callValue:
49        continue
50    if "unknown2" in callValue:
51        continue
52    if "unknown3" in callValue:
53        continue
54    if "unknown4" in callValue:
55        continue
56    if "pure" in callValue:
57        continue
58    srcloc = callInfo[1]
59    if srcloc.startswith("workdir/"): continue
60    # ignore Qt stuff
61    if srcloc.startswith("Gui/"): continue
62    if srcloc.startswith("Widgets/"): continue
63    if srcloc.startswith("Core/"): continue
64    if srcloc.startswith("/Qt"): continue
65    if srcloc.startswith("Qt"): continue
66    if srcloc.startswith("64-"): continue
67    functionSig = callInfo[0]
68    tmp1list.append((srcloc, functionSig, callValue))
69
70def merge_bitfield(a, b):
71    if len(a) == 0: return b
72    ret = ""
73    for i, c in enumerate(b):
74        if c == "1" or a[i] == "1":
75            ret += "1"
76        else:
77            ret += "0"
78    return ret;
79tmp2dict = dict()
80tmp2list = list()
81for paramInfo in paramSet:
82    name = paramInfo[0]
83    bitfield = paramInfo[1]
84    if re.match( r"\w+ com::", name): continue
85    if re.match( r"\w+ ooo::vba::", name): continue
86    if re.match( r"\w+ orcus::", name): continue
87    if re.match( r"\w+ std::", name): continue
88    if not name in tmp2dict:
89        tmp2dict[name] = bitfield
90    else:
91        tmp2dict[name] = merge_bitfield(tmp2dict[name], bitfield)
92for name, bitfield in iter(tmp2dict.items()):
93    srcloc = definitionToSourceLocationMap[name]
94    # ignore Qt stuff
95    if srcloc.startswith("Gui/"): continue
96    if srcloc.startswith("Widgets/"): continue
97    if srcloc.startswith("Core/"): continue
98    if srcloc.startswith("/Qt"): continue
99    if srcloc.startswith("Qt"): continue
100    if srcloc.startswith("64-"): continue
101    # ignore external stuff
102    if srcloc.startswith("workdir/"): continue
103    # referenced by generated code in workdir/
104    if srcloc.startswith("writerfilter/source/ooxml/OOXMLFactory.hxx"): continue
105    if "0" in bitfield:
106        tmp2list.append((srcloc, name, bitfield))
107
108# sort results by filename:lineno
109def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
110    return [int(text) if text.isdigit() else text.lower()
111            for text in re.split(_nsre, s)]
112# sort by both the source-line and the datatype, so the output file ordering is stable
113# when we have multiple items on the same source line
114def v_sort_key(v):
115    return natural_sort_key(v[0]) + [v[1]]
116tmp1list.sort(key=lambda v: v_sort_key(v))
117tmp2list.sort(key=lambda v: v_sort_key(v))
118
119# print out the results
120with open("compilerplugins/clang/virtualdead.results", "wt") as f:
121    for v in tmp1list:
122        f.write(v[0] + "\n")
123        f.write("    " + v[1] + "\n")
124        f.write("    " + v[2] + "\n")
125with open("compilerplugins/clang/virtualdead.unusedparams.results", "wt") as f:
126    for v in tmp2list:
127        f.write(v[0] + "\n")
128        f.write("    " + v[1] + "\n")
129        f.write("    " + v[2] + "\n")
130