constantparam.py 7.23 KB
Newer Older
1 2 3 4 5 6
#!/usr/bin/python

import sys
import re
import io

7
callDict = dict() # callInfo tuple -> callValue
8 9 10 11 12 13 14

# clang does not always use exactly the same numbers in the type-parameter vars it generates
# so I need to substitute them to ensure we can match correctly.
normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
def normalizeTypeParams( line ):
    return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)

15
# reading as binary (since we known it is pure ascii) is much faster than reading as unicode
16
with io.open("workdir/loplugin.constantparam.log", "rb", buffering=1024*1024) as txt:
17
    for line in txt:
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
        try:
            tokens = line.strip().split("\t")
            returnType = normalizeTypeParams(tokens[0])
            nameAndParams = normalizeTypeParams(tokens[1])
            sourceLocation = tokens[2]
            paramName = tokens[3]
            paramType = normalizeTypeParams(tokens[4])
            callValue = tokens[5]
            callInfo = (returnType, nameAndParams, paramName, paramType, sourceLocation)
            if not callInfo in callDict:
                callDict[callInfo] = set()
            callDict[callInfo].add(callValue)
        except IndexError:
            print "problem with line " + line.strip()
            raise

def RepresentsInt(s):
    try:
        int(s)
        return True
    except ValueError:
        return False

41
constructor_regex = re.compile("^\w+\(\)$")
42 43

tmp1list = list()
44 45
tmp2list = list()
tmp3list = list()
Noel Grandin's avatar
Noel Grandin committed
46
tmp4list = list()
Noel Grandin's avatar
Noel Grandin committed
47
for callInfo, callValues in callDict.iteritems():
48 49 50
    nameAndParams = callInfo[1]
    if len(callValues) != 1:
        continue
51 52
    callValue = next(iter(callValues))
    if "unknown" in callValue:
53
        continue
Noel Grandin's avatar
Noel Grandin committed
54 55 56
    sourceLoc = callInfo[4]
    functionSig = callInfo[0] + " " + callInfo[1]

57
    # try to ignore setter methods
58
    if ("," not in nameAndParams) and (("::set" in nameAndParams) or ("::Set" in nameAndParams)):
59
        continue
Noel Grandin's avatar
Noel Grandin committed
60 61 62 63 64 65 66 67 68 69
    # ignore code that follows a common pattern
    if sourceLoc.startswith("sw/inc/swatrset.hxx"): continue
    if sourceLoc.startswith("sw/inc/format.hxx"): continue
    # template generated code
    if sourceLoc.startswith("include/sax/fshelper.hxx"): continue
    # debug code
    if sourceLoc.startswith("include/oox/dump"): continue
    # part of our binary API
    if sourceLoc.startswith("include/LibreOfficeKit"): continue

70 71 72 73 74
    # ignore methods generated by SFX macros
    if "RegisterInterface(class SfxModule *)" in nameAndParams: continue
    if "RegisterChildWindow(_Bool,class SfxModule *,enum SfxChildWindowFlags)" in nameAndParams: continue
    if "RegisterControl(unsigned short,class SfxModule *)" in nameAndParams: continue

75 76 77 78 79 80
    if RepresentsInt(callValue):
        if callValue == "0" or callValue == "1":
            tmp1list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue))
        else:
            tmp2list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue))
    # look for places where the callsite is always a constructor invocation
81
    elif constructor_regex.match(callValue) or callValue == "\"\"":
82 83 84 85
        if callValue.startswith("Get"): continue
        if callValue.startswith("get"): continue
        if "operator=" in functionSig: continue
        if "&&" in functionSig: continue
Noel Grandin's avatar
Noel Grandin committed
86 87 88
        if callInfo[2] == "###0" and callValue == "InitData()": continue
        if callInfo[2] == "###0" and callValue == "InitAggregate()": continue
        if callValue == "shared_from_this()": continue
89
        tmp3list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue))
Noel Grandin's avatar
Noel Grandin committed
90 91
    else:
        tmp4list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue))
92

93

94
# sort results by filename:lineno
95 96 97
def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
    return [int(text) if text.isdigit() else text.lower()
            for text in re.split(_nsre, s)]
Noel Grandin's avatar
Noel Grandin committed
98
tmp1list.sort(key=lambda v: natural_sort_key(v[0]))
99 100
tmp2list.sort(key=lambda v: natural_sort_key(v[0]))
tmp3list.sort(key=lambda v: natural_sort_key(v[0]))
Noel Grandin's avatar
Noel Grandin committed
101
tmp4list.sort(key=lambda v: natural_sort_key(v[0]))
102 103

# print out the results
Noel Grandin's avatar
Noel Grandin committed
104
with open("compilerplugins/clang/constantparam.booleans.results", "wt") as f:
105
    for v in tmp1list:
Noel Grandin's avatar
Noel Grandin committed
106
        f.write(v[0] + "\n")
107
        f.write("    " + v[1] + "\n")
Noel Grandin's avatar
Noel Grandin committed
108
        f.write("    " + v[2] + "\n")
109
        f.write("    " + v[3] + "\n")
Noel Grandin's avatar
Noel Grandin committed
110
with open("compilerplugins/clang/constantparam.numbers.results", "wt") as f:
111 112 113 114 115
    for v in tmp2list:
        f.write(v[0] + "\n")
        f.write("    " + v[1] + "\n")
        f.write("    " + v[2] + "\n")
        f.write("    " + v[3] + "\n")
Noel Grandin's avatar
Noel Grandin committed
116
with open("compilerplugins/clang/constantparam.constructors.results", "wt") as f:
117 118 119 120 121
    for v in tmp3list:
        f.write(v[0] + "\n")
        f.write("    " + v[1] + "\n")
        f.write("    " + v[2] + "\n")
        f.write("    " + v[3] + "\n")
Noel Grandin's avatar
Noel Grandin committed
122 123 124 125 126 127
with open("compilerplugins/clang/constantparam.others.results", "wt") as f:
    for v in tmp4list:
        f.write(v[0] + "\n")
        f.write("    " + v[1] + "\n")
        f.write("    " + v[2] + "\n")
        f.write("    " + v[3] + "\n")
128

129 130 131 132
# -------------------------------------------------------------
# Now a fun set of heuristics to look for methods that
# take bitmask parameters where one or more of the bits in the
# bitmask is always one or always zero
133

Andrea Gelmini's avatar
Andrea Gelmini committed
134
# integer to hex str
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
def hex(i):
    return "0x%x" % i
# I can't use python's ~ operator, because that produces negative numbers
def negate(i):
    return (1 << 32) - 1 - i

tmp2list = list()
for callInfo, callValues in callDict.iteritems():
    nameAndParams = callInfo[1]
    if len(callValues) < 2:
        continue
    # we are only interested in enum parameters
    if not "enum" in callInfo[3]: continue
    if not "Flag" in callInfo[3] and not "flag" in callInfo[3] and not "Bit" in callInfo[3] and not "State" in callInfo[3]: continue
    # try to ignore setter methods
    if ("," not in nameAndParams) and (("::set" in nameAndParams) or ("::Set" in nameAndParams)):
        continue

    setBits = 0
    clearBits = 0
    continue_flag = False
    first = True
    for callValue in callValues:
        if "unknown" == callValue or not callValue.isdigit():
            continue_flag = True
            break
        if first:
            setBits = int(callValue)
            clearBits = negate(int(callValue))
            first = False
        else:
            setBits = setBits & int(callValue)
            clearBits = clearBits & negate(int(callValue))

    # estimate allBits by using the highest bit we have seen
    # TODO dump more precise information about the allBits values of enums
    allBits = (1 << setBits.bit_length()) - 1
    clearBits = clearBits & allBits
    if continue_flag or (setBits == 0 and clearBits == 0): continue

    sourceLoc = callInfo[4]
    functionSig = callInfo[0] + " " + callInfo[1]

    v2 = callInfo[3] + " " + callInfo[2]
    if setBits != 0: v2 += " setBits=" + hex(setBits)
    if clearBits != 0: v2 += " clearBits=" + hex(clearBits)
    tmp2list.append((sourceLoc, functionSig, v2))


# sort results by filename:lineno
tmp2list.sort(key=lambda v: natural_sort_key(v[0]))

# print out the results
Noel Grandin's avatar
Noel Grandin committed
188
with open("compilerplugins/clang/constantparam.bitmask.results", "wt") as f:
189 190 191 192
    for v in tmp2list:
        f.write(v[0] + "\n")
        f.write("    " + v[1] + "\n")
        f.write("    " + v[2] + "\n")