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