cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / expandablemethods.py
blobac97c2b0869936453dacbd323642d47c8facfc4b
1 #!/usr/bin/python
3 import sys
4 import re
5 import io
7 # --------------------------------------------------------------------------------------------
8 # globals
9 # --------------------------------------------------------------------------------------------
11 definitionSet = set() # set of tuple(return_type, name_and_params)
12 definitionToSourceLocationMap = dict()
13 calledFromDict = dict()
14 calledFromOutsideSet = set()
15 largeFunctionSet = set() # set of tuple(return_type, name_and_params)
16 addressOfSet = set() # set of tuple(return_type, name_and_params)
18 # clang does not always use exactly the same numbers in the type-parameter vars it generates
19 # so I need to substitute them to ensure we can match correctly.
20 normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
21 def normalizeTypeParams( line ):
22 return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
24 # --------------------------------------------------------------------------------------------
25 # primary input loop
26 # --------------------------------------------------------------------------------------------
28 # The parsing here is designed to avoid grabbing stuff which is mixed in from gbuild.
29 # I have not yet found a way of suppressing the gbuild output.
30 with io.open("loplugin.expandablemethods.log", "rb", buffering=1024*1024) as txt:
31 for line in txt:
32 tokens = line.strip().split("\t")
33 if tokens[0] == "definition:":
34 access = tokens[1]
35 returnType = tokens[2]
36 nameAndParams = tokens[3]
37 sourceLocation = tokens[4]
38 funcInfo = (normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams))
39 definitionSet.add(funcInfo)
40 definitionToSourceLocationMap[funcInfo] = sourceLocation
41 elif tokens[0] == "calledFrom:":
42 calleeLocation = tokens[1]
43 returnType = tokens[2]
44 nameAndParams = tokens[3]
45 funcInfo = (normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams))
46 if not funcInfo in calledFromDict:
47 calledFromDict[funcInfo] = set()
48 calledFromDict[funcInfo].add(calleeLocation)
49 elif tokens[0] == "outside:":
50 returnType = tokens[1]
51 nameAndParams = tokens[2]
52 calledFromOutsideSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
53 elif tokens[0] == "large:":
54 returnType = tokens[1]
55 nameAndParams = tokens[2]
56 largeFunctionSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
57 elif tokens[0] == "addrof:":
58 returnType = tokens[1]
59 nameAndParams = tokens[2]
60 addressOfSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
61 else:
62 print( "unknown line: " + line)
64 # Invert the definitionToSourceLocationMap.
65 # If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
66 # and we should just ignore it.
67 sourceLocationToDefinitionMap = {}
68 for k, v in definitionToSourceLocationMap.iteritems():
69 sourceLocationToDefinitionMap[v] = sourceLocationToDefinitionMap.get(v, [])
70 sourceLocationToDefinitionMap[v].append(k)
71 for k, definitions in sourceLocationToDefinitionMap.iteritems():
72 if len(definitions) > 1:
73 for d in definitions:
74 definitionSet.remove(d)
76 def isOtherConstness( d, callSet ):
77 method = d[0] + " " + d[1]
78 # if this method is const, and there is a non-const variant of it, and the non-const variant is in use, then leave it alone
79 if d[0].startswith("const ") and d[1].endswith(" const"):
80 if ((d[0][6:],d[1][:-6]) in callSet):
81 return True
82 elif method.endswith(" const"):
83 method2 = method[:len(method)-6] # strip off " const"
84 if ((d[0],method2) in callSet):
85 return True
86 if method.endswith(" const") and ("::iterator" in method):
87 method2 = method[:len(method)-6] # strip off " const"
88 method2 = method2.replace("::const_iterator", "::iterator")
89 if ((d[0],method2) in callSet):
90 return True
91 # if this method is non-const, and there is a const variant of it, and the const variant is in use, then leave it alone
92 if (not method.endswith(" const")) and ((d[0],"const " + method + " const") in callSet):
93 return True
94 if (not method.endswith(" const")) and ("::iterator" in method):
95 method2 = method.replace("::iterator", "::const_iterator") + " const"
96 if ((d[0],method2) in callSet):
97 return True
98 return False
100 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
101 def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
102 return [int(text) if text.isdigit() else text.lower()
103 for text in re.split(_nsre, s)]
104 def sort_set_by_natural_key(s):
105 return sorted(s, key=lambda v: natural_sort_key(v[1]))
108 # --------------------------------------------------------------------------------------------
109 # Methods that are only called from inside their own class, and are only called from one spot
110 # --------------------------------------------------------------------------------------------
112 tmp4set = set()
113 for d in definitionSet:
114 if d in calledFromOutsideSet:
115 continue
116 if isOtherConstness(d, calledFromOutsideSet):
117 continue
118 if d not in definitionToSourceLocationMap:
119 print("warning, method has no location: " + d[0] + " " + d[1])
120 continue
121 # ignore external code
122 if definitionToSourceLocationMap[d].startswith("external/"):
123 continue
124 # ignore constructors, calledFromOutsideSet does not provide accurate info for them
125 tokens = d[1].split("(")[0].split("::")
126 if len(tokens)>1 and tokens[-2] == tokens[-1]:
127 continue
128 # ignore large methods, which make the code clearer by being out of line
129 if d in largeFunctionSet:
130 continue
131 # ignore methods whose address we take
132 if d in addressOfSet:
133 continue
134 # ignore unused methods, leave them to the dedicated analysis
135 if d not in calledFromDict:
136 continue
137 # ignore methods called from more than one site
138 if len(calledFromDict[d]) > 1:
139 continue
141 method = d[0] + " " + d[1]
142 tmp4set.add((method, definitionToSourceLocationMap[d]))
144 # print output, sorted by name and line number
145 with open("loplugin.expandablemethods.report", "wt") as f:
146 for t in sort_set_by_natural_key(tmp4set):
147 f.write(t[1] + "\n")
148 f.write(" " + t[0] + "\n")