bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / unusedfields.py
blob6bd1550163921156fa60b446dce500b3070fa49d
1 #!/usr/bin/python
3 import sys
4 import re
5 import io
7 definitionSet = set()
8 protectedAndPublicDefinitionSet = set() # set of tuple(type, name)
9 definitionToSourceLocationMap = dict()
10 definitionToTypeMap = dict()
11 touchedFromInsideSet = set()
12 touchedFromOutsideSet = set()
13 touchedFromOutsideConstructorSet = set()
14 readFromSet = set()
15 writeToSet = set()
16 sourceLocationSet = set()
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 def parseFieldInfo( tokens ):
25 if len(tokens) == 3:
26 return (normalizeTypeParams(tokens[1]), tokens[2])
27 else:
28 return (normalizeTypeParams(tokens[1]), "")
30 with io.open("workdir/loplugin.unusedfields.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 fieldInfo = (normalizeTypeParams(tokens[2]), tokens[3])
36 srcLoc = tokens[5]
37 # ignore external source code
38 if (srcLoc.startswith("external/")):
39 continue
40 # ignore build folder
41 if (srcLoc.startswith("workdir/")):
42 continue
43 definitionSet.add(fieldInfo)
44 definitionToTypeMap[fieldInfo] = tokens[4]
45 if access == "protected" or access == "public":
46 protectedAndPublicDefinitionSet.add(fieldInfo)
47 definitionToSourceLocationMap[fieldInfo] = tokens[5]
48 elif tokens[0] == "inside:":
49 touchedFromInsideSet.add(parseFieldInfo(tokens))
50 elif tokens[0] == "outside:":
51 touchedFromOutsideSet.add(parseFieldInfo(tokens))
52 elif tokens[0] == "outside-constructor:":
53 touchedFromOutsideConstructorSet.add(parseFieldInfo(tokens))
54 elif tokens[0] == "read:":
55 readFromSet.add(parseFieldInfo(tokens))
56 elif tokens[0] == "write:":
57 writeToSet.add(parseFieldInfo(tokens))
58 else:
59 print( "unknown line: " + line)
61 # Calculate untouched
62 untouchedSet = set()
63 untouchedSetD = set()
64 for d in definitionSet:
65 if d in touchedFromOutsideSet or d in touchedFromInsideSet:
66 continue
67 srcLoc = definitionToSourceLocationMap[d];
68 # this is all representations of on-disk data structures
69 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
70 or srcLoc.startswith("sw/source/filter/ww8/")
71 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
72 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
73 or srcLoc.startswith("vcl/inc/unx/XIM.h")
74 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
75 or srcLoc.startswith("include/svl/svdde.hxx")
76 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
77 or srcLoc.startswith("hwpfilter/")
78 or srcLoc.startswith("embeddedobj/source/inc/")
79 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")
80 or srcLoc.startswith("bridges/")):
81 continue
82 if d[0] in set([ "AtkObjectWrapperClass", "AtkObjectWrapper", "GLOMenu", "GLOAction", "_XRegion", "SalMenuButtonItem", "Vertex",
83 "OOoMountOperationClass", "SwCSS1ItemIds", "ScCompiler::AddInMap", "MemoryByteGrabber", "textcat_t", "fp_t", "ngram_t",
84 "ImplPPTParaPropSet", "DataNode"]):
85 continue
86 # unit testing code
87 if srcLoc.startswith("cppu/source/uno/check.cxx"):
88 continue
89 fieldType = definitionToTypeMap[d]
90 if "ModuleClient" in fieldType:
91 continue
92 # leave the weld stuff alone until Caolan is finished
93 if "weld::" in fieldType:
94 continue
95 if "::sfx2::sidebar::ControllerItem" in fieldType:
96 continue
97 untouchedSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc))
98 untouchedSetD.add(d)
100 # Calculate only-touched-in-constructor set
101 onlyUsedInConstructorSet = set()
102 for d in definitionSet:
103 if d in touchedFromOutsideSet or d in touchedFromOutsideConstructorSet:
104 continue
105 srcLoc = definitionToSourceLocationMap[d];
106 # this is all representations of on-disk data structures
107 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
108 or srcLoc.startswith("sw/source/filter/ww8/")
109 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
110 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
111 or srcLoc.startswith("vcl/inc/unx/XIM.h")
112 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
113 or srcLoc.startswith("include/svl/svdde.hxx")
114 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
115 or srcLoc.startswith("hwpfilter/")
116 or srcLoc.startswith("embeddedobj/source/inc/")
117 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")
118 or srcLoc.startswith("bridges/")):
119 continue
120 fieldType = definitionToTypeMap[d]
121 if "std::unique_ptr" in fieldType:
122 continue
123 if "std::shared_ptr" in fieldType:
124 continue
125 if "Reference<" in fieldType:
126 continue
127 if "VclPtr<" in fieldType:
128 continue
129 # leave the weld stuff alone until Caolan is finished
130 if "weld::" in fieldType:
131 continue
132 if "osl::Mutex" in fieldType:
133 continue
134 if "::sfx2::sidebar::ControllerItem" in fieldType:
135 continue
136 onlyUsedInConstructorSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc))
138 writeonlySet = set()
139 for d in definitionSet:
140 parentClazz = d[0];
141 if d in readFromSet or d in untouchedSetD:
142 continue
143 srcLoc = definitionToSourceLocationMap[d];
144 # this is all representations of on-disk data structures
145 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
146 or srcLoc.startswith("sw/source/filter/ww8/")
147 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
148 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
149 or srcLoc.startswith("vcl/inc/unx/XIM.h")
150 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
151 or srcLoc.startswith("include/svl/svdde.hxx")
152 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
153 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")):
154 continue
155 fieldType = definitionToTypeMap[d]
156 if "ModuleClient" in fieldType:
157 continue
158 if "::sfx2::sidebar::ControllerItem" in fieldType:
159 continue
160 # ignore reference fields, because writing to them actually writes to another field somewhere else
161 if fieldType.endswith("&"):
162 continue
163 # ignore the import/export data model stuff
164 if srcLoc.startswith("sc/source/filter/inc/") and "Model" in fieldType:
165 continue
166 if srcLoc.startswith("sc/source/filter/inc/") and (parentClazz.startswith("Xcl") or parentClazz.startswith("oox::xls::")):
167 continue
168 # implement some kind of registration of errors
169 if fieldType == "class SfxErrorHandler *":
170 continue
171 # mutex locking
172 if "Guard" in fieldType:
173 continue
174 # leave the weld stuff alone until Caolan is finished
175 if "weld::" in fieldType:
176 continue
177 # these are just all model classes
178 if (srcLoc.startswith("oox/")
179 or srcLoc.startswith("lotuswordpro/")
180 or srcLoc.startswith("include/oox/")
181 or srcLoc.startswith("include/filter/")
182 or srcLoc.startswith("hwpfilter/")
183 or srcLoc.startswith("filter/")):
184 continue
186 writeonlySet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc))
189 readonlySet = set()
190 for d in definitionSet:
191 parentClazz = d[0];
192 if d in writeToSet or d in untouchedSetD:
193 continue
194 fieldType = definitionToTypeMap[d]
195 srcLoc = definitionToSourceLocationMap[d];
196 if "ModuleClient" in fieldType:
197 continue
198 # this is all representations of on-disk data structures
199 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
200 or srcLoc.startswith("sw/source/filter/ww8/")
201 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
202 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
203 or srcLoc.startswith("vcl/inc/unx/XIM.h")
204 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
205 or srcLoc.startswith("include/svl/svdde.hxx")):
206 continue
207 # I really don't care about these ancient file formats
208 if (srcLoc.startswith("hwpfilter/")
209 or srcLoc.startswith("lotuswordpro/")):
210 continue
211 # leave the weld stuff alone until Caolan is finished
212 if "weld::" in fieldType:
213 continue
214 readonlySet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc))
217 canBePrivateSet = set()
218 for d in protectedAndPublicDefinitionSet:
219 clazz = d[0] + " " + d[1]
220 if d in touchedFromOutsideSet:
221 continue
222 srcLoc = definitionToSourceLocationMap[d];
224 canBePrivateSet.add((clazz + " " + definitionToTypeMap[d], srcLoc))
227 # --------------------------------------------------------------------------------------------
228 # "all fields in class can be made private" analysis
229 # --------------------------------------------------------------------------------------------
231 potentialClasses = set()
232 excludedClasses = set()
233 potentialClassesSourceLocationMap = dict()
234 matchClassName = re.compile(r"(\w+)::")
235 for d in protectedAndPublicDefinitionSet:
236 clazz = d[0]
237 if d in touchedFromOutsideSet:
238 excludedClasses.add(clazz)
239 else:
240 potentialClasses.add(clazz)
241 potentialClassesSourceLocationMap[clazz] = definitionToSourceLocationMap[d]
242 allFieldsCanBePrivateSet = set()
243 for d in (potentialClasses - excludedClasses):
244 sourceLoc = potentialClassesSourceLocationMap[d]
245 # when the class is inside a compile unit, assume that the compiler can figure this out for itself, much less interesting to me
246 if not ".cxx" in sourceLoc:
247 allFieldsCanBePrivateSet.add((d, sourceLoc))
249 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
250 def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
251 return [int(text) if text.isdigit() else text.lower()
252 for text in re.split(_nsre, s)]
254 # sort results by name and line number
255 tmp1list = sorted(untouchedSet, key=lambda v: natural_sort_key(v[1]))
256 tmp2list = sorted(writeonlySet, key=lambda v: natural_sort_key(v[1]))
257 tmp3list = sorted(canBePrivateSet, key=lambda v: natural_sort_key(v[1]))
258 tmp4list = sorted(readonlySet, key=lambda v: natural_sort_key(v[1]))
259 tmp5list = sorted(onlyUsedInConstructorSet, key=lambda v: natural_sort_key(v[1]))
260 tmp6list = sorted(allFieldsCanBePrivateSet, key=lambda v: natural_sort_key(v[1]))
262 # print out the results
263 with open("compilerplugins/clang/unusedfields.untouched.results", "wt") as f:
264 for t in tmp1list:
265 f.write( t[1] + "\n" )
266 f.write( " " + t[0] + "\n" )
267 with open("compilerplugins/clang/unusedfields.writeonly.results", "wt") as f:
268 for t in tmp2list:
269 f.write( t[1] + "\n" )
270 f.write( " " + t[0] + "\n" )
271 # this one is not checked in yet because I haven't actually done anything with it
272 with open("loplugin.unusedfields.report-can-be-private", "wt") as f:
273 for t in tmp3list:
274 f.write( t[1] + "\n" )
275 f.write( " " + t[0] + "\n" )
276 with open("compilerplugins/clang/unusedfields.readonly.results", "wt") as f:
277 for t in tmp4list:
278 f.write( t[1] + "\n" )
279 f.write( " " + t[0] + "\n" )
280 with open("compilerplugins/clang/unusedfields.only-used-in-constructor.results", "wt") as f:
281 for t in tmp5list:
282 f.write( t[1] + "\n" )
283 f.write( " " + t[0] + "\n" )
284 with open("compilerplugins/clang/unusedfields.report-all-can-be-private", "wt") as f:
285 for t in tmp6list:
286 f.write( t[1] + "\n" )
287 f.write( " " + t[0] + "\n" )