cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / unusedmethods.py
blob53cc934955550c05d7406471c7f80b0ff7931f72
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()
14 # for the "unused methods" analysis
15 callSet = set() # set of tuple(return_type, name_and_params)
17 # for the "method can be private" analysis
18 publicDefinitionSet = set() # set of tuple(return_type, name_and_params)
19 calledFromOutsideSet = set() # set of tuple(return_type, name_and_params)
20 virtualSet = set() # set of tuple(return_type, name_and_params)
22 # for the "unused return types" analysis
23 usedReturnSet = set() # set of tuple(return_type, name_and_params)
26 # things we need to exclude for reasons like :
27 # - it's a weird template thingy that confuses the plugin
28 unusedMethodsExclusionSet = set([
29 # only used by Windows build
30 "_Bool basegfx::B2ITuple::equalZero() const",
31 "class basegfx::B2DPolyPolygon basegfx::unotools::UnoPolyPolygon::getPolyPolygonUnsafe() const",
32 "void basegfx::B2IRange::expand(const class basegfx::B2IRange &)",
33 "void OpenGLContext::requestSingleBufferedRendering()",
34 "_Bool TabitemValue::isBothAligned() const",
35 "_Bool TabitemValue::isNotAligned() const",
36 "void TabitemValue::isLast() const",
37 "void StyleSettings::SetSpinSize(long)",
38 "void StyleSettings::SetFloatTitleHeight(long)",
39 "void StyleSettings::SetTitleHeight(long)",
40 "void StyleSettings::SetUseFlatBorders(_Bool)",
41 "void StyleSettings::SetUseFlatMenus(_Bool)",
42 "void StyleSettings::SetCursorSize(long)",
43 "_Bool CommandMediaData::GetPassThroughToOS() const",
44 "void Application::AppEvent(const class ApplicationEvent &)",
45 "int PhysicalFontFace::GetWidth() const",
46 "void PhysicalFontFace::SetBitmapSize(int,int)",
47 "class boost::intrusive_ptr<class FontCharMap> FontCharMap::GetDefaultMap(_Bool)",
48 "_Bool SalObject::IsEraseBackgroundEnabled()",
49 "const class rtl::OUString & connectivity::OColumn::getCatalogName() const",
50 "const class rtl::OUString & connectivity::OColumn::getSchemaName() const",
51 "_Bool connectivity::OColumn::isDefinitelyWritable() const",
52 "_Bool connectivity::OColumn::isReadOnly() const",
53 "_Bool connectivity::OColumn::isWritable() const",
54 "_Bool IDocumentLinksAdministration::GetData(const class rtl::OUString &,const class rtl::OUString &,class com::sun::star::uno::Any &) const",
55 "_Bool IDocumentLinksAdministration::SetData(const class rtl::OUString &,const class rtl::OUString &,const class com::sun::star::uno::Any &)",
56 "_Bool ScImportExport::ImportData(const class rtl::OUString &,const class com::sun::star::uno::Any &)",
57 "void* ScannerManager::GetData()",
58 "void ScannerManager::SetData(void *)",
59 # only used by OSX build
60 "void StyleSettings::SetHideDisabledMenuItems(_Bool)",
61 # debugging methods
62 "void oox::drawingml::TextParagraphProperties::dump() const",
63 "void oox::PropertyMap::dumpCode(class com::sun::star::uno::Reference<class com::sun::star::beans::XPropertySet>)",
64 "void oox::PropertyMap::dumpData(class com::sun::star::uno::Reference<class com::sun::star::beans::XPropertySet>)",
65 "class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > writerfilter::ooxml::OOXMLPropertySet::toString()",
66 # I need to teach the plugin that for loops with range expressions call begin() and end()
67 "class __gnu_debug::_Safe_iterator<class __gnu_cxx::__normal_iterator<class SwAnchoredObject *const *, class std::__cxx1998::vector<class SwAnchoredObject *, class std::allocator<class SwAnchoredObject *> > >, class std::__debug::vector<class SwAnchoredObject *, class std::allocator<class SwAnchoredObject *> > > SwSortedObjs::begin() const",
68 "class __gnu_debug::_Safe_iterator<class __gnu_cxx::__normal_iterator<class SwAnchoredObject *const *, class std::__cxx1998::vector<class SwAnchoredObject *, class std::allocator<class SwAnchoredObject *> > >, class std::__debug::vector<class SwAnchoredObject *, class std::allocator<class SwAnchoredObject *> > > SwSortedObjs::end() const",
69 # loaded by dlopen()
70 "void * getStandardAccessibleFactory()",
71 "void * getSvtAccessibilityComponentFactory()",
72 "struct _rtl_uString * basicide_choose_macro(void *,void *,unsigned char,struct _rtl_uString *)",
73 "void basicide_macro_organizer(short)",
74 "long basicide_handle_basic_error(void *)",
75 "class com::sun::star::uno::XInterface * org_libreoffice_chart2_Chart2ToolboxController(class com::sun::star::uno::XComponentContext *,const class com::sun::star::uno::Sequence<class com::sun::star::uno::Any> &)",
76 "class com::sun::star::uno::XInterface * org_libreoffice_comp_chart2_sidebar_ChartPanelFactory(class com::sun::star::uno::XComponentContext *,const class com::sun::star::uno::Sequence<class com::sun::star::uno::Any> &)",
77 "class chart::opengl::OpenglShapeFactory * getOpenglShapeFactory()",
78 "class VclAbstractDialogFactory * CreateDialogFactory()",
79 "_Bool GetSpecialCharsForEdit(class vcl::Window *,const class vcl::Font &,class rtl::OUString &)",
80 "const struct ImplTextEncodingData * sal_getFullTextEncodingData(unsigned short)",
81 "class SalInstance * create_SalInstance()",
82 "class SwAbstractDialogFactory * SwCreateDialogFactory()",
83 "class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface> WordPerfectImportFilterDialog_createInstance(const class com::sun::star::uno::Reference<class com::sun::star::uno::XComponentContext> &)",
84 "class UnoWrapperBase * CreateUnoWrapper()",
85 "class SwAbstractDialogFactory * SwCreateDialogFactory()",
86 "unsigned long GetSaveWarningOfMSVBAStorage_ww8(class SfxObjectShell &)",
87 "unsigned long SaveOrDelMSVBAStorage_ww8(class SfxObjectShell &,class SotStorage &,unsigned char,const class rtl::OUString &)",
88 "void ExportRTF(const class rtl::OUString &,const class rtl::OUString &,class tools::SvRef<class Writer> &)",
89 "void ExportDOC(const class rtl::OUString &,const class rtl::OUString &,class tools::SvRef<class Writer> &)",
90 "class Reader * ImportRTF()",
91 "void ImportXE(class SwDoc &,class SwPaM &,const class rtl::OUString &)",
92 "_Bool TestImportDOC(const class rtl::OUString &,const class rtl::OUString &)",
93 "class vcl::Window * CreateWindow(class VCLXWindow **,const struct com::sun::star::awt::WindowDescriptor *,class vcl::Window *,long)",
94 # only used when the ODBC driver is enabled
95 "_Bool getImplementation(type-parameter-?-? *&,const class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface> &)",
98 # clang does not always use exactly the same numbers in the type-parameter vars it generates
99 # so I need to substitute them to ensure we can match correctly.
100 normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
101 def normalizeTypeParams( line ):
102 return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
104 # --------------------------------------------------------------------------------------------
105 # primary input loop
106 # --------------------------------------------------------------------------------------------
108 # The parsing here is designed to avoid grabbing stuff which is mixed in from gbuild.
109 # I have not yet found a way of suppressing the gbuild output.
110 with io.open("loplugin.unusedmethods.log", "rb", buffering=1024*1024) as txt:
111 for line in txt:
112 tokens = line.strip().split("\t")
113 if tokens[0] == "definition:":
114 access = tokens[1]
115 returnType = tokens[2]
116 nameAndParams = tokens[3]
117 sourceLocation = tokens[4]
118 virtual = ""
119 if len(tokens)>=6: virtual = tokens[5]
120 funcInfo = (normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams))
121 definitionSet.add(funcInfo)
122 if access == "public":
123 publicDefinitionSet.add(funcInfo)
124 definitionToSourceLocationMap[funcInfo] = sourceLocation
125 if virtual == "virtual":
126 virtualSet.add(funcInfo)
127 elif tokens[0] == "call:":
128 returnType = tokens[1]
129 nameAndParams = tokens[2]
130 callSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
131 elif tokens[0] == "usedReturn:":
132 returnType = tokens[1]
133 nameAndParams = tokens[2]
134 usedReturnSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
135 elif tokens[0] == "outside:":
136 returnType = tokens[1]
137 nameAndParams = tokens[2]
138 calledFromOutsideSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
139 else:
140 print( "unknown line: " + line)
142 # Invert the definitionToSourceLocationMap.
143 # If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
144 # and we should just ignore it.
145 sourceLocationToDefinitionMap = {}
146 for k, v in definitionToSourceLocationMap.iteritems():
147 sourceLocationToDefinitionMap[v] = sourceLocationToDefinitionMap.get(v, [])
148 sourceLocationToDefinitionMap[v].append(k)
149 for k, definitions in sourceLocationToDefinitionMap.iteritems():
150 if len(definitions) > 1:
151 for d in definitions:
152 definitionSet.remove(d)
154 def isOtherConstness( d, callSet ):
155 method = d[0] + " " + d[1]
156 # 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
157 if d[0].startswith("const ") and d[1].endswith(" const"):
158 if ((d[0][6:],d[1][:-6]) in callSet):
159 return True
160 elif method.endswith(" const"):
161 method2 = method[:len(method)-6] # strip off " const"
162 if ((d[0],method2) in callSet):
163 return True
164 if method.endswith(" const") and ("::iterator" in method):
165 method2 = method[:len(method)-6] # strip off " const"
166 method2 = method2.replace("::const_iterator", "::iterator")
167 if ((d[0],method2) in callSet):
168 return True
169 # 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
170 if (not method.endswith(" const")) and ((d[0],"const " + method + " const") in callSet):
171 return True
172 if (not method.endswith(" const")) and ("::iterator" in method):
173 method2 = method.replace("::iterator", "::const_iterator") + " const"
174 if ((d[0],method2) in callSet):
175 return True
176 return False
178 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
179 def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
180 return [int(text) if text.isdigit() else text.lower()
181 for text in re.split(_nsre, s)]
182 def sort_set_by_natural_key(s):
183 return sorted(s, key=lambda v: natural_sort_key(v[1]))
186 # --------------------------------------------------------------------------------------------
187 # "unused methods" analysis
188 # --------------------------------------------------------------------------------------------
190 tmp1set = set() # set of tuple(method, source_location)
191 unusedSet = set() # set of tuple(return_type, name_and_params)
192 for d in definitionSet:
193 method = d[0] + " " + d[1]
194 if method in unusedMethodsExclusionSet:
195 continue
196 if d in callSet:
197 continue
198 if isOtherConstness(d, callSet):
199 continue
200 # include assigment operators, if we remove them, the compiler creates a default one, which can have odd consequences
201 if "::operator=(" in d[1]:
202 continue
203 # these are only invoked implicitly, so the plugin does not see the calls
204 if "::operator new(" in d[1] or "::operator delete(" in d[1]:
205 continue
206 # just ignore iterators, they normally occur in pairs, and we typically want to leave one constness version alone
207 # alone if the other one is in use.
208 if d[1] == "begin() const" or d[1] == "begin()" or d[1] == "end()" or d[1] == "end() const":
209 continue
210 # There is lots of macro magic going on in SRCDIR/include/sax/fshelper.hxx that should be using C++11 varag templates
211 if d[1].startswith("sax_fastparser::FastSerializerHelper::"):
212 continue
213 # used by Windows build
214 if any(x in d[1] for x in ["DdeTopic::", "DdeData::", "DdeService::", "DdeTransaction::", "DdeConnection::", "DdeLink::", "DdeItem::", "DdeGetPutItem::"]):
215 continue
216 if method == "class tools::SvRef<class FontCharMap> FontCharMap::GetDefaultMap(_Bool)":
217 continue
218 # too much template magic here for my plugin
219 if ( ("cairocanvas::" in d[1])
220 or ("canvas::" in d[1])
221 or ("oglcanvas::" in d[1])
222 or ("vclcanvas::" in d[1])):
223 continue
224 # these are loaded by dlopen() from somewhere
225 if "get_implementation" in d[1]:
226 continue
227 if "component_getFactory" in d[1]:
228 continue
229 if d[0]=="_Bool" and "_supportsService(const class rtl::OUString &)" in d[1]:
230 continue
231 if (d[0]=="class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>"
232 and "Instance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)" in d[1]):
233 continue
234 # ignore the Java symbols, loaded from the JavaVM
235 if d[1].startswith("Java_"):
236 continue
237 # ignore external code
238 if definitionToSourceLocationMap[d].startswith("external/"):
239 continue
240 # ignore the VCL_BUILDER_DECL_FACTORY stuff
241 if d[0]=="void" and d[1].startswith("make") and ("(class VclPtr<class vcl::Window> &" in d[1]):
242 continue
243 # ignore methods used to dump objects to stream - normally used for debugging
244 if d[0] == "class std::basic_ostream<char> &" and d[1].startswith("operator<<(class std::basic_ostream<char> &"):
245 continue
246 if d[0] == "basic_ostream<type-parameter-?-?, type-parameter-?-?> &" and d[1].startswith("operator<<(basic_ostream<type-parameter-?-?"):
247 continue
248 # ignore the SfxPoolItem CreateDefault methods for now
249 if d[1].endswith("::CreateDefault()"):
250 continue
251 if "::operator" in d[1]:
252 continue
254 location = definitionToSourceLocationMap[d];
255 # whacky template stuff
256 if location.startswith("sc/source/ui/vba/vbaformat.hxx"): continue
257 # not sure how this stuff is called
258 if location.startswith("include/test"): continue
259 # leave the debug/dump alone
260 if location.startswith("include/oox/dump"): continue
262 unusedSet.add(d) # used by the "unused return types" analysis
263 tmp1set.add((method, location))
265 # print out the results, sorted by name and line number
266 with open("loplugin.unusedmethods.report-unused-methods", "wt") as f:
267 for t in sort_set_by_natural_key(tmp1set):
268 f.write(t[1] + "\n")
269 f.write(" " + t[0] + "\n")
271 # --------------------------------------------------------------------------------------------
272 # "unused return types" analysis
273 # --------------------------------------------------------------------------------------------
275 tmp2set = set()
276 for d in definitionSet:
277 method = d[0] + " " + d[1]
278 if d in usedReturnSet:
279 continue
280 if d in unusedSet:
281 continue
282 if isOtherConstness(d, usedReturnSet):
283 continue
284 # ignore methods with no return type, and constructors
285 if d[0] == "void" or d[0] == "":
286 continue
287 # ignore bool returns, provides important documentation in the code
288 if d[0] == "_Bool":
289 continue
290 # ignore UNO constructor method entrypoints
291 if "_get_implementation" in d[1] or "_getFactory" in d[1]:
292 continue
293 # the plugin can't see calls to these
294 if "operator new" in d[1]:
295 continue
296 # unused return type is not a problem here
297 if ("operator=(" in d[1] or "operator&=" in d[1] or "operator|=" in d[1] or "operator^=" in d[1]
298 or "operator+=" in d[1] or "operator-=" in d[1]
299 or "operator<<" in d[1] or "operator>>" in d[1]
300 or "operator++" in d[1] or "operator--" in d[1]):
301 continue
302 # ignore external code
303 if definitionToSourceLocationMap[d].startswith("external/"):
304 continue
305 # ignore the SfxPoolItem CreateDefault methods for now
306 if d[1].endswith("::CreateDefault()"):
307 continue
308 # ignore UNO constructor functions
309 if (d[0] == "class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>" and
310 d[1].endswith("_createInstance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)")):
311 continue
312 if (d[0] == "class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>" and
313 d[1].endswith("_CreateInstance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)")):
314 continue
315 # debug code
316 if d[1] == "writerfilter::ooxml::OOXMLPropertySet::toString()":
317 continue
318 location = definitionToSourceLocationMap[d];
319 # windows only
320 if location.startswith("include/svl/svdde.hxx"): continue
321 # fluent API (return ref to self)
322 if location.startswith("include/tools/stream.hxx"): continue
323 tmp2set.add((method, location))
325 # print output, sorted by name and line number
326 with open("loplugin.unusedmethods.report-unused-returns", "wt") as f:
327 for t in sort_set_by_natural_key(tmp2set):
328 f.write(t[1] + "\n")
329 f.write(" " + t[0] + "\n")
332 # --------------------------------------------------------------------------------------------
333 # "method can be private" analysis
334 # --------------------------------------------------------------------------------------------
336 tmp3set = set()
337 for d in publicDefinitionSet:
338 method = d[0] + " " + d[1]
339 if d in calledFromOutsideSet:
340 continue
341 if d in virtualSet:
342 continue
343 # TODO ignore constructors for now, my called-from-outside analysis doesn't work here
344 if d[0] == "":
345 continue
346 if isOtherConstness(d, calledFromOutsideSet):
347 continue
348 # ignore external code
349 if definitionToSourceLocationMap[d].startswith("external/"):
350 continue
351 tmp3set.add((method, definitionToSourceLocationMap[d]))
353 # print output, sorted by name and line number
354 with open("loplugin.unusedmethods.report-can-be-private", "wt") as f:
355 for t in sort_set_by_natural_key(tmp3set):
356 f.write(t[1] + "\n")
357 f.write(" " + t[0] + "\n")