6 # --------------------------------------------------------------------------------------------
8 # --------------------------------------------------------------------------------------------
10 definitionSet
= set() # set of tuple(return_type, name_and_params)
11 definitionToSourceLocationMap
= dict()
13 # for the "unused methods" analysis
14 callSet
= set() # set of tuple(return_type, name_and_params)
16 # for the "method can be private" analysis
17 publicDefinitionSet
= set() # set of tuple(return_type, name_and_params)
18 protectedDefinitionSet
= 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)
25 # clang does not always use exactly the same numbers in the type-parameter vars it generates
26 # so I need to substitute them to ensure we can match correctly.
27 normalizeTypeParamsRegex1
= re
.compile(r
"type-parameter-\d+-\d+")
28 # clang sometimes generates a type name as either "class Foo" or "Foo"
29 # so I need to substitute them to ensure we can match correctly.
30 normalizeTypeParamsRegex2
= re
.compile(r
"class ")
31 def normalizeTypeParams( line
):
32 line
= normalizeTypeParamsRegex1
.sub("type-parameter-?-?", line
)
33 line
= normalizeTypeParamsRegex2
.sub("", line
)
36 # --------------------------------------------------------------------------------------------
38 # --------------------------------------------------------------------------------------------
40 with io
.open("workdir/loplugin.unusedmethods.log", "r", buffering
=16*1024*1024) as txt
:
42 tokens
= line
.strip().split("\t")
43 if tokens
[0] == "definition:":
45 returnType
= tokens
[2]
46 nameAndParams
= tokens
[3]
47 sourceLocation
= tokens
[4]
49 if len(tokens
)>=6: virtual
= tokens
[5]
50 funcInfo
= (normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
))
51 definitionSet
.add(funcInfo
)
52 if access
== "public":
53 publicDefinitionSet
.add(funcInfo
)
54 elif access
== "protected":
55 protectedDefinitionSet
.add(funcInfo
)
56 definitionToSourceLocationMap
[funcInfo
] = sourceLocation
57 if virtual
== "virtual":
58 virtualSet
.add(funcInfo
)
59 elif tokens
[0] == "call:":
60 returnType
= tokens
[1]
61 nameAndParams
= tokens
[2]
62 callSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
63 elif tokens
[0] == "usedReturn:":
64 returnType
= tokens
[1]
65 nameAndParams
= tokens
[2]
66 usedReturnSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
67 elif tokens
[0] == "outside:":
68 returnType
= tokens
[1]
69 nameAndParams
= tokens
[2]
70 calledFromOutsideSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
72 print( "unknown line: " + line
)
74 # Invert the definitionToSourceLocationMap.
75 sourceLocationToDefinitionMap
= {}
76 for k
, v
in definitionToSourceLocationMap
.items():
77 sourceLocationToDefinitionMap
[v
] = sourceLocationToDefinitionMap
.get(v
, [])
78 sourceLocationToDefinitionMap
[v
].append(k
)
80 def isOtherConstness( d
, callSet
):
81 method
= d
[0] + " " + d
[1]
82 # 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
83 if d
[0].startswith("const ") and d
[1].endswith(" const"):
84 if ((d
[0][6:],d
[1][:-6]) in callSet
):
86 elif method
.endswith(" const"):
87 method2
= method
[:len(method
)-6] # strip off " const"
88 if ((d
[0],method2
) in callSet
):
90 if method
.endswith(" const") and ("::iterator" in method
):
91 method2
= method
[:len(method
)-6] # strip off " const"
92 method2
= method2
.replace("::const_iterator", "::iterator")
93 if ((d
[0],method2
) in callSet
):
95 # 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
96 if (not method
.endswith(" const")) and ((d
[0],"const " + method
+ " const") in callSet
):
98 if (not method
.endswith(" const")) and ("::iterator" in method
):
99 method2
= method
.replace("::iterator", "::const_iterator") + " const"
100 if ((d
[0],method2
) in callSet
):
104 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
105 def natural_sort_key(s
, _nsre
=re
.compile('([0-9]+)')):
106 return [int(text
) if text
.isdigit() else text
.lower()
107 for text
in re
.split(_nsre
, s
)]
108 # sort by both the source-line and the datatype, so the output file ordering is stable
109 # when we have multiple items on the same source line
111 return natural_sort_key(v
[1]) + [v
[0]]
112 def sort_set_by_natural_key(s
):
113 return sorted(s
, key
=lambda v
: v_sort_key(v
))
116 # --------------------------------------------------------------------------------------------
117 # "unused methods" analysis
118 # --------------------------------------------------------------------------------------------
120 tmp1set
= set() # set of tuple(method, source_location)
121 unusedSet
= set() # set of tuple(return_type, name_and_params)
122 for d
in definitionSet
:
123 method
= d
[0] + " " + d
[1]
126 if isOtherConstness(d
, callSet
):
128 # exclude assignment operators, if we remove them, the compiler creates a default one, which can have odd consequences
129 if "::operator=(" in d
[1]:
131 # these are only invoked implicitly, so the plugin does not see the calls
132 if "::operator new(" in d
[1] or "::operator delete(" in d
[1]:
134 # just ignore iterators, they normally occur in pairs, and we typically want to leave one constness version alone
135 # alone if the other one is in use.
136 if d
[1] == "begin() const" or d
[1] == "begin()" or d
[1] == "end()" or d
[1] == "end() const":
138 # used by Windows build
139 if any(x
in d
[1] for x
in ["DdeTopic::", "DdeData::", "DdeService::", "DdeTransaction::", "DdeConnection::", "DdeLink::", "DdeItem::", "DdeGetPutItem::"]):
141 if method
== "class tools::SvRef<class FontCharMap> FontCharMap::GetDefaultMap(_Bool)":
143 # these are loaded by dlopen() from somewhere
144 if "get_implementation" in d
[1]:
146 if "component_getFactory" in d
[1]:
148 if d
[0]=="_Bool" and "_supportsService(const class rtl::OUString &)" in d
[1]:
150 if (d
[0]=="class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>"
151 and "Instance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)" in d
[1]):
153 # ignore the Java symbols, loaded from the JavaVM
154 if d
[1].startswith("Java_"):
156 # ignore the VCL_BUILDER_DECL_FACTORY stuff
157 if d
[0]=="void" and d
[1].startswith("make") and ("(class VclPtr<class vcl::Window> &" in d
[1]):
159 # ignore methods used to dump objects to stream - normally used for debugging
160 if d
[0] == "class std::basic_ostream<char> &" and d
[1].startswith("operator<<(class std::basic_ostream<char> &"):
162 if d
[0] == "basic_ostream<type-parameter-?-?, type-parameter-?-?> &" and d
[1].startswith("operator<<(basic_ostream<type-parameter-?-?"):
165 if (" ::operator " in method
) or (" ::__invoke(" in method
) or (" ::operator())" in method
): continue
166 if ("(lambda at " in method
): continue
167 # ignore stuff generated by std::function parameters
168 if ("(anonymous)::operator " in method
) and ("(*)" in method
): continue
169 # stuff generated by Qt
170 if "::tr(" in method
or "::trUtf8(" in method
: continue
172 location
= definitionToSourceLocationMap
[d
];
173 # whacky template stuff
174 if location
.startswith("sc/source/ui/vba/vbaformat.hxx"): continue
175 # not sure how this stuff is called
176 if location
.startswith("include/test"): continue
177 # leave the debug/dump alone
178 if location
.startswith("include/oox/dump"): continue
179 # plugin testing stuff
180 if location
.startswith("compilerplugins/clang/test"): continue
181 # leave this alone for now
182 if location
.startswith("include/LibreOfficeKit"): continue
184 if location
.startswith("include/vcl/vclptr.hxx"): continue
185 if location
.startswith("include/oox/helper/refvector.hxx"): continue
186 if location
.startswith("include/oox/drawingml/chart/modelbase.hxx"): continue
188 unusedSet
.add(d
) # used by the "unused return types" analysis
189 tmp1set
.add((method
, location
))
191 # print out the results, sorted by name and line number
192 with
open("compilerplugins/clang/unusedmethods.results", "wt") as f
:
193 for t
in sort_set_by_natural_key(tmp1set
):
195 f
.write(" " + t
[0] + "\n")
197 # --------------------------------------------------------------------------------------------
198 # "unused return types" analysis
199 # --------------------------------------------------------------------------------------------
202 for d
in definitionSet
:
203 method
= d
[0] + " " + d
[1]
204 if d
in usedReturnSet
:
208 if isOtherConstness(d
, usedReturnSet
):
210 # ignore methods with no return type, and constructors
211 if d
[0] == "void" or d
[0] == "":
213 # ignore UNO constructor method entrypoints
214 if "_get_implementation" in d
[1] or "_getFactory" in d
[1]:
216 # the plugin can't see calls to these
217 if "::operator new" in d
[1]:
219 # unused return type is not a problem here
220 if ("operator=(" in d
[1] or "operator&=" in d
[1] or "operator|=" in d
[1] or "operator^=" in d
[1]
221 or "operator+=" in d
[1] or "operator-=" in d
[1]
222 or "operator<<" in d
[1] or "operator>>" in d
[1]
223 or "operator++" in d
[1] or "operator--" in d
[1]):
225 # ignore UNO constructor functions
226 if (d
[0] == "class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>" and
227 d
[1].endswith("_createInstance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)")):
229 if (d
[0] == "class com::sun::star::uno::Reference<class com::sun::star::uno::XInterface>" and
230 d
[1].endswith("_CreateInstance(const class com::sun::star::uno::Reference<class com::sun::star::lang::XMultiServiceFactory> &)")):
233 if d
[1] == "writerfilter::ooxml::OOXMLPropertySet::toString()":
236 if "::__invoke(" in d
[1]:
238 if "(lambda at " in d
[1]:
240 if "::operator " in d
[1] and "(*)(" in d
[1]:
242 location
= definitionToSourceLocationMap
[d
];
244 if location
.startswith("include/svl/svdde.hxx"): continue
245 # fluent API (return ref to self)
246 if location
.startswith("include/tools/stream.hxx"): continue
247 if location
.startswith("include/oox/helper/refvector.hxx"): continue
248 if location
.startswith("include/oox/drawingml/chart/modelbase.hxx"): continue
250 if location
.startswith("include/vcl/vclptr.hxx"): continue
252 if location
.startswith("include/LibreOfficeKit/LibreOfficeKit.hxx"): continue
253 tmp2set
.add((method
, location
))
255 #Disable this for now, not really using it
256 # print output, sorted by name and line number
257 with
open("compilerplugins/clang/unusedmethods.unused-returns.results", "wt") as f
:
258 for t
in sort_set_by_natural_key(tmp2set
):
260 f
.write(" " + t
[0] + "\n")
263 # --------------------------------------------------------------------------------------------
264 # "method can be private" analysis
265 # --------------------------------------------------------------------------------------------
268 for d
in publicDefinitionSet
:
269 method
= d
[0] + " " + d
[1]
270 if d
in calledFromOutsideSet
:
274 # TODO ignore constructors for now, my called-from-outside analysis doesn't work here
277 if isOtherConstness(d
, calledFromOutsideSet
):
279 tmp3set
.add((method
, definitionToSourceLocationMap
[d
]))
281 # print output, sorted by name and line number
282 with
open("loplugin.unusedmethods.report-can-be-private", "wt") as f
:
283 for t
in sort_set_by_natural_key(tmp3set
):
285 f
.write(" " + t
[0] + "\n")
289 # --------------------------------------------------------------------------------------------
290 # "all protected methods in class can be made private" analysis
291 # --------------------------------------------------------------------------------------------
293 potentialClasses
= set()
294 excludedClasses
= set()
295 potentialClassesSourceLocationMap
= dict()
296 matchClassName
= re
.compile(r
"(\w+)::")
297 for d
in protectedDefinitionSet
:
298 m
= matchClassName
.match(d
[1])
301 if d
in calledFromOutsideSet
:
302 excludedClasses
.add(clazz
)
304 potentialClasses
.add(clazz
)
305 potentialClassesSourceLocationMap
[clazz
] = definitionToSourceLocationMap
[d
]
308 for d
in (potentialClasses
- excludedClasses
):
309 tmp4set
.add((d
, potentialClassesSourceLocationMap
[d
]))
311 # print output, sorted by name and line number
312 with
open("loplugin.unusedmethods.report-all-protected-can-be-private", "wt") as f
:
313 for t
in sort_set_by_natural_key(tmp4set
):
315 f
.write(" " + t
[0] + "\n")