Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / unusedfields.py
blob072284576a6a1ee22612fd9082c6d81f4b65f256
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 # 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 # Calculate untouched
74 untouchedSet = set()
75 untouchedSetD = set()
76 for d in definitionSet:
77 if d in touchedFromOutsideSet or d in touchedFromInsideSet:
78 continue
79 srcLoc = definitionToSourceLocationMap[d];
80 # this is all representations of on-disk data structures
81 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
82 or srcLoc.startswith("sw/source/filter/ww8/")
83 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
84 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
85 or srcLoc.startswith("vcl/inc/unx/XIM.h")
86 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
87 or srcLoc.startswith("include/svl/svdde.hxx")
88 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
89 or srcLoc.startswith("hwpfilter/")
90 or srcLoc.startswith("embeddedobj/source/inc/")
91 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")
92 or srcLoc.startswith("bridges/")):
93 continue
94 if d[0] in set([ "AtkObjectWrapperClass", "AtkObjectWrapper", "GLOMenu", "GLOAction", "_XRegion", "SalMenuButtonItem", "Vertex",
95 "OOoMountOperationClass", "SwCSS1ItemIds", "ScCompiler::AddInMap", "MemoryByteGrabber", "textcat_t", "fp_t", "ngram_t",
96 "ImplPPTParaPropSet", "DataNode"]):
97 continue
98 # unit testing code
99 if srcLoc.startswith("cppu/source/uno/check.cxx"):
100 continue
101 fieldType = definitionToTypeMap[d]
102 if "ModuleClient" in fieldType:
103 continue
104 if "::sfx2::sidebar::ControllerItem" in fieldType:
105 continue
106 untouchedSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc))
107 untouchedSetD.add(d)
109 # Calculate only-touched-in-constructor set
110 onlyUsedInConstructorSet = set()
111 for d in definitionSet:
112 if d in touchedFromOutsideSet or d in touchedFromOutsideConstructorSet:
113 continue
114 srcLoc = definitionToSourceLocationMap[d];
115 # this is all representations of on-disk data structures
116 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
117 or srcLoc.startswith("sw/source/filter/ww8/")
118 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
119 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
120 or srcLoc.startswith("vcl/inc/unx/XIM.h")
121 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
122 or srcLoc.startswith("include/svl/svdde.hxx")
123 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
124 or srcLoc.startswith("hwpfilter/")
125 or srcLoc.startswith("embeddedobj/source/inc/")
126 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")
127 or srcLoc.startswith("bridges/")):
128 continue
129 fieldType = definitionToTypeMap[d]
130 if "std::unique_ptr" in fieldType:
131 continue
132 if "std::shared_ptr" in fieldType:
133 continue
134 if "Reference<" in fieldType:
135 continue
136 if "VclPtr<" in fieldType:
137 continue
138 if "osl::Mutex" in fieldType:
139 continue
140 if "::sfx2::sidebar::ControllerItem" in fieldType:
141 continue
142 onlyUsedInConstructorSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc))
144 writeonlySet = set()
145 for d in definitionSet:
146 parentClazz = d[0];
147 if d in readFromSet or d in untouchedSetD:
148 continue
149 srcLoc = definitionToSourceLocationMap[d];
150 # this is all representations of on-disk data structures
151 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
152 or srcLoc.startswith("sw/source/filter/ww8/")
153 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
154 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
155 or srcLoc.startswith("vcl/inc/unx/XIM.h")
156 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
157 or srcLoc.startswith("include/svl/svdde.hxx")
158 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx")
159 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")):
160 continue
161 fieldType = definitionToTypeMap[d]
162 if "ModuleClient" in fieldType:
163 continue
164 if "::sfx2::sidebar::ControllerItem" in fieldType:
165 continue
166 # ignore reference fields, because writing to them actually writes to another field somewhere else
167 if fieldType.endswith("&"):
168 continue
169 # ignore the import/export data model stuff
170 if srcLoc.startswith("sc/source/filter/inc/") and "Model" in fieldType:
171 continue
172 if srcLoc.startswith("sc/source/filter/inc/") and (parentClazz.startswith("Xcl") or parentClazz.startswith("oox::xls::")):
173 continue
174 # implement some kind of registration of errors
175 if fieldType == "class SfxErrorHandler *":
176 continue
177 # mutex locking
178 if "Guard" in fieldType:
179 continue
180 # these are just all model classes
181 if (srcLoc.startswith("oox/")
182 or srcLoc.startswith("lotuswordpro/")
183 or srcLoc.startswith("include/oox/")
184 or srcLoc.startswith("include/filter/")
185 or srcLoc.startswith("hwpfilter/")
186 or srcLoc.startswith("filter/")):
187 continue
189 writeonlySet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc))
192 readonlySet = set()
193 for d in definitionSet:
194 parentClazz = d[0];
195 if d in writeToSet or d in untouchedSetD:
196 continue
197 fieldType = definitionToTypeMap[d]
198 srcLoc = definitionToSourceLocationMap[d];
199 if "ModuleClient" in fieldType:
200 continue
201 # this is all representations of on-disk data structures
202 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx")
203 or srcLoc.startswith("sw/source/filter/ww8/")
204 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx")
205 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx")
206 or srcLoc.startswith("vcl/inc/unx/XIM.h")
207 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h")
208 or srcLoc.startswith("include/svl/svdde.hxx")):
209 continue
210 # I really don't care about these ancient file formats
211 if (srcLoc.startswith("hwpfilter/")
212 or srcLoc.startswith("lotuswordpro/")):
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 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
228 def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
229 return [int(text) if text.isdigit() else text.lower()
230 for text in re.split(_nsre, s)]
232 # sort results by name and line number
233 tmp1list = sorted(untouchedSet, key=lambda v: natural_sort_key(v[1]))
234 tmp2list = sorted(writeonlySet, key=lambda v: natural_sort_key(v[1]))
235 tmp3list = sorted(canBePrivateSet, key=lambda v: natural_sort_key(v[1]))
236 tmp4list = sorted(readonlySet, key=lambda v: natural_sort_key(v[1]))
237 tmp5list = sorted(onlyUsedInConstructorSet, key=lambda v: natural_sort_key(v[1]))
239 # print out the results
240 with open("compilerplugins/clang/unusedfields.untouched.results", "wt") as f:
241 for t in tmp1list:
242 f.write( t[1] + "\n" )
243 f.write( " " + t[0] + "\n" )
244 with open("compilerplugins/clang/unusedfields.writeonly.results", "wt") as f:
245 for t in tmp2list:
246 f.write( t[1] + "\n" )
247 f.write( " " + t[0] + "\n" )
248 # this one is not checked in yet because I haven't actually done anything with it
249 with open("loplugin.unusedfields.report-can-be-private", "wt") as f:
250 for t in tmp3list:
251 f.write( t[1] + "\n" )
252 f.write( " " + t[0] + "\n" )
253 with open("compilerplugins/clang/unusedfields.readonly.results", "wt") as f:
254 for t in tmp4list:
255 f.write( t[1] + "\n" )
256 f.write( " " + t[0] + "\n" )
257 with open("compilerplugins/clang/unusedfields.only-used-in-constructor.results", "wt") as f:
258 for t in tmp5list:
259 f.write( t[1] + "\n" )
260 f.write( " " + t[0] + "\n" )