bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / checkunusedparams.cxx
blob2d4bc0dd60fecc529cb645483aa595fef04a89ee
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <cassert>
11 #include <string>
12 #include <set>
13 #include <iostream>
15 #include "plugin.hxx"
17 /**
18 Find parameters that have no name, i.e. they are unused and we're worked around the "unused parameter" warning.
20 Most of these can be removed.
22 TODO look for places where we are working around the warning by doing
23 (void) param1;
25 namespace {
27 class CheckUnusedParams: public loplugin::FilteringPlugin<CheckUnusedParams> {
28 public:
29 explicit CheckUnusedParams(loplugin::InstantiationData const & data):
30 FilteringPlugin(data) {}
31 void run() override;
32 bool VisitFunctionDecl(FunctionDecl const *);
33 bool VisitUnaryAddrOf(UnaryOperator const *);
34 bool VisitInitListExpr(InitListExpr const *);
35 bool VisitCallExpr(CallExpr const *);
36 bool VisitBinAssign(BinaryOperator const *);
37 bool VisitCXXConstructExpr(CXXConstructExpr const *);
38 private:
39 void checkForFunctionDecl(Expr const *, bool bCheckOnly = false);
40 std::set<FunctionDecl const *> m_addressOfSet;
41 enum class PluginPhase { FindAddressOf, Warning };
42 PluginPhase m_phase;
45 void CheckUnusedParams::run()
47 StringRef fn(handler.getMainFileName());
48 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/"))
49 return;
50 // Taking pointer to function
51 if (loplugin::isSamePathname(fn, SRCDIR "/l10ntools/source/xmlparse.cxx"))
52 return;
53 // macro magic which declares something needed by an external library
54 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/misc/gridprinter.cxx"))
55 return;
57 // valid test/qa code
58 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/compilerplugins/clang/test/"))
59 return;
60 if (loplugin::isSamePathname(fn, SRCDIR "/cppu/qa/test_reference.cxx"))
61 return;
63 // leave this alone for now
64 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit/"))
65 return;
66 // this has a certain pattern to its code which appears to include lots of unused params
67 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/xmloff/"))
68 return;
69 // I believe someone is busy working on this chunk of code
70 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/dataprovider.cxx"))
71 return;
72 // I think erack is working on stuff here
73 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/excel/xiformula.cxx"))
74 return;
75 // lots of callbacks here
76 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/lotus/op.cxx"))
77 return;
78 // template magic
79 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/html/htmlpars.cxx"))
80 return;
82 m_phase = PluginPhase::FindAddressOf;
83 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
84 m_phase = PluginPhase::Warning;
85 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
88 bool CheckUnusedParams::VisitUnaryAddrOf(UnaryOperator const * op) {
89 if (m_phase != PluginPhase::FindAddressOf)
90 return true;
91 checkForFunctionDecl(op->getSubExpr());
92 return true;
95 bool CheckUnusedParams::VisitBinAssign(BinaryOperator const * binaryOperator) {
96 if (m_phase != PluginPhase::FindAddressOf)
97 return true;
98 checkForFunctionDecl(binaryOperator->getRHS());
99 return true;
102 bool CheckUnusedParams::VisitCallExpr(CallExpr const * callExpr) {
103 if (m_phase != PluginPhase::FindAddressOf)
104 return true;
105 for (auto arg : callExpr->arguments())
106 checkForFunctionDecl(arg);
107 return true;
110 bool CheckUnusedParams::VisitCXXConstructExpr(CXXConstructExpr const * constructExpr) {
111 if (m_phase != PluginPhase::FindAddressOf)
112 return true;
113 for (auto arg : constructExpr->arguments())
114 checkForFunctionDecl(arg);
115 return true;
118 bool CheckUnusedParams::VisitInitListExpr(InitListExpr const * initListExpr) {
119 if (m_phase != PluginPhase::FindAddressOf)
120 return true;
121 for (auto subStmt : *initListExpr)
122 checkForFunctionDecl(dyn_cast<Expr>(subStmt));
123 return true;
126 void CheckUnusedParams::checkForFunctionDecl(Expr const * expr, bool bCheckOnly) {
127 auto e1 = expr->IgnoreParenCasts();
128 auto declRef = dyn_cast<DeclRefExpr>(e1);
129 if (!declRef)
130 return;
131 auto functionDecl = dyn_cast<FunctionDecl>(declRef->getDecl());
132 if (!functionDecl)
133 return;
134 if (bCheckOnly)
135 getParentStmt(expr)->dump();
136 else
137 m_addressOfSet.insert(functionDecl->getCanonicalDecl());
140 static int noFieldsInRecord(RecordType const * recordType) {
141 auto recordDecl = recordType->getDecl();
142 // if it's complicated, lets just assume it has fields
143 if (isa<ClassTemplateSpecializationDecl>(recordDecl))
144 return 1;
145 return std::distance(recordDecl->field_begin(), recordDecl->field_end());
147 static bool startswith(const std::string& rStr, const char* pSubStr) {
148 return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
150 static bool endswith(const std::string& rStr, const char* pSubStr) {
151 auto len = strlen(pSubStr);
152 if (len > rStr.size())
153 return false;
154 return rStr.compare(rStr.size() - len, rStr.size(), pSubStr) == 0;
157 bool CheckUnusedParams::VisitFunctionDecl(FunctionDecl const * decl) {
158 if (m_phase != PluginPhase::Warning)
159 return true;
160 if (m_addressOfSet.find(decl->getCanonicalDecl()) != m_addressOfSet.end())
161 return true;
162 if (ignoreLocation(decl))
163 return true;
164 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(decl->getLocation())))
165 return true;
167 auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(decl);
168 if (cxxMethodDecl) {
169 if (cxxMethodDecl->isVirtual())
170 return true;
171 auto cxxConstructorDecl = dyn_cast<CXXConstructorDecl>(cxxMethodDecl);
172 if (cxxConstructorDecl && cxxConstructorDecl->isCopyOrMoveConstructor())
173 return true;
175 if (!decl->isThisDeclarationADefinition())
176 return true;
177 if (decl->isFunctionTemplateSpecialization())
178 return true;
179 if (decl->isDeleted())
180 return true;
181 if (decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate)
182 return true;
183 if (decl->isOverloadedOperator())
184 return true;
185 if (decl->isExternC())
186 return true;
188 //TODO, filtering out any functions relating to class templates for now:
189 CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
190 if (r != nullptr
191 && (r->getTemplateSpecializationKind() != TSK_Undeclared
192 || r->isDependentContext()))
194 return true;
196 FunctionDecl const * canon = decl->getCanonicalDecl();
197 std::string fqn = canon->getQualifiedNameAsString(); // because sometimes clang returns nonsense for the filename of canon
198 if (ignoreLocation(canon))
199 return true;
200 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(canon->getLocation())))
201 return true;
202 StringRef fn = getFileNameOfSpellingLoc(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(canon)));
203 // Some backwards compat magic.
204 // TODO Can probably be removed, but need to do some checking
205 if (loplugin::isSamePathname(fn, SRCDIR "/include/sax/fshelper.hxx"))
206 return true;
207 // Platform-specific code
208 if (loplugin::isSamePathname(fn, SRCDIR "/include/svl/svdde.hxx"))
209 return true;
210 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/svmain.hxx"))
211 return true;
212 // passing pointer to function
213 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/bitmapaccess.hxx"))
214 return true;
215 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtkobject.hxx"))
216 return true;
217 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtksalframe.hxx"))
218 return true;
219 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtkframe.hxx"))
220 return true;
221 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx"))
222 return true;
223 if (loplugin::isSamePathname(fn, SRCDIR "/extensions/source/propctrlr/propertyeditor.hxx"))
224 return true;
225 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/solar/inc/navtoolbar.hxx"))
226 return true;
227 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/grammar.cxx"))
228 return true;
229 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/lexer.cxx"))
230 return true;
231 // marked with a TODO/FIXME
232 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/sallayout.hxx"))
233 return true;
234 if (loplugin::isSamePathname(fn, SRCDIR "/accessibility/inc/standard/vclxaccessiblelist.hxx"))
235 return true;
236 // these are "extern C" but clang doesn't seem to report that accurately
237 if (loplugin::isSamePathname(fn, SRCDIR "/sax/source/fastparser/fastparser.cxx"))
238 return true;
239 // these all follow the same pattern, seems a pity to break that
240 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/graphicfilter.hxx"))
241 return true;
242 // looks like work in progress
243 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument.cxx"))
244 return true;
245 // macro magic
246 if (loplugin::isSamePathname(fn, SRCDIR "/basctl/source/inc/basidesh.hxx"))
247 return true;
248 // template magic
249 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/canvas/"))
250 return true;
251 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/canvas/"))
252 return true;
253 if (loplugin::isSamePathname(fn, SRCDIR "/include/comphelper/unwrapargs.hxx"))
254 return true;
255 // this looks like vaguely useful code (ParseError) that I'm loathe to remove
256 if (loplugin::isSamePathname(fn, SRCDIR "/connectivity/source/inc/RowFunctionParser.hxx"))
257 return true;
258 if (loplugin::isSamePathname(fn, SRCDIR "/include/svx/EnhancedCustomShapeFunctionParser.hxx"))
259 return true;
260 // TODO marker parameter in constructor, should probably be using an enum
261 if (loplugin::isSamePathname(fn, SRCDIR "/framework/inc/uielement/uicommanddescription.hxx"))
262 return true;
263 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/SlideTransitionPane.hxx"))
264 return true;
265 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/animations/CustomAnimationPane.hxx"))
266 return true;
267 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/table/TableDesignPane.hxx"))
268 return true;
269 // debug stuff
270 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/core/data/column2.cxx"))
271 return true;
272 // weird stuff
273 if (loplugin::isSamePathname(fn, SRCDIR "/scaddins/source/analysis/analysishelper.hxx"))
274 return true;
275 // SFX_DECL_CHILDWINDOWCONTEXT macro stuff
276 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/NavigatorChildWindow.hxx"))
277 return true;
278 // TODO, need to remove this from the .sdi file too
279 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/SlideSorterViewShell.hxx"))
280 return true;
281 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/OutlineViewShell.hxx"))
282 return true;
283 // SFX_DECL_INTERFACE macro stuff
284 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/ViewShellBase.hxx"))
285 return true;
286 // debug stuff
287 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/ppt/pptinanimations.hxx"))
288 return true;
289 // takes pointer to fn
290 if (loplugin::isSamePathname(fn, SRCDIR "/include/sfx2/shell.hxx"))
291 return true;
292 // TODO, need to remove this from the .sdi file too
293 if (fqn == "SfxObjectShell::StateView_Impl")
294 return true;
295 // SFX_DECL_CHILDWINDOW_WITHID macro
296 if (loplugin::isSamePathname(fn, SRCDIR "/include/sfx2/infobar.hxx"))
297 return true;
298 // this looks like vaguely useful code (ParseError) that I'm loathe to remove
299 if (loplugin::isSamePathname(fn, SRCDIR "/slideshow/source/inc/slideshowexceptions.hxx"))
300 return true;
301 // SFX_DECL_VIEWFACTORY macro
302 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/inc/view.hxx"))
303 return true;
304 // debugging
305 if (fqn == "BrowseBox::DoShowCursor" || fqn == "BrowseBox::DoHideCursor")
306 return true;
307 // if I change this one, it then overrides a superclass virtual method
308 if (fqn == "GalleryBrowser2::KeyInput")
309 return true;
310 // takes pointer to function
311 if (fqn == "cmis::AuthProvider::onedriveAuthCodeFallback" || fqn == "cmis::AuthProvider::gdriveAuthCodeFallback")
312 return true;
313 if (fqn == "ooo_mount_operation_ask_password")
314 return true;
315 // TODO tricky to remove because of default params
316 if (fqn == "xmloff::OAttribute2Property::addBooleanProperty")
317 return true;
318 // taking pointer to function
319 if (fqn == "sw::DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl"
320 || fqn == "sw::DocumentContentOperationsManager::DeleteRangeImpl"
321 || fqn == "SwTableFormula::GetFormulaBoxes"
322 || fqn == "SwFEShell::Drag"
323 || fqn == "GetASCWriter" || fqn == "GetHTMLWriter" || fqn == "GetXMLWriter"
324 || fqn == "SwWrtShell::UpdateLayoutFrame" || fqn == "SwWrtShell::DefaultDrag"
325 || fqn == "SwWrtShell::DefaultEndDrag"
326 || startswith(fqn, "SwWW8ImplReader::Read_"))
327 return true;
328 // WIN32 only
329 if (fqn == "SwFntObj::GuessLeading")
330 return true;
331 // SFX_DECL_CHILDWINDOW_WITHID macro
332 if (fqn == "SwSpellDialogChildWindow::SwSpellDialogChildWindow"
333 || fqn == "SwFieldDlgWrapper::SwFieldDlgWrapper"
334 || fqn == "SwInputChild::SwInputChild")
335 return true;
336 // SFX_DECL_VIEWFACTORY macro
337 if (fqn == "SwSrcView::SwSrcView")
338 return true;
339 // Serves to disambiguate two very similar methods
340 if (fqn == "MSWordStyles::BuildGetSlot")
341 return true;
342 // TODO there are just too many default params to make this worth fixing right now
343 if (fqn == "ScDocument::CopyMultiRangeFromClip")
344 return true;
345 // TODO looks like this needs fixing?
346 if (fqn == "ScTable::ExtendPrintArea")
347 return true;
348 // there is a FIXME in the code
349 if (fqn == "ScRangeUtil::IsAbsTabArea")
350 return true;
351 // SFX_DECL_CHILDWINDOW_WITHID
352 if (fqn == "ScInputWindowWrapper::ScInputWindowWrapper"
353 || fqn == "sc::SearchResultsDlgWrapper::SearchResultsDlgWrapper")
354 return true;
355 // ExecMethod in .sdi file
356 if (fqn == "ScChartShell::ExecuteExportAsGraphic")
357 return true;
358 // bool marker parameter
359 if (fqn == "SvxIconReplacementDialog::SvxIconReplacementDialog")
360 return true;
361 // used as pointer to fn
362 if (endswith(fqn, "_createInstance"))
363 return true;
364 // callback
365 if (startswith(fqn, "SbRtl_"))
366 return true;
367 // takes pointer to fn
368 if (fqn == "migration::BasicMigration_create" || fqn == "migration::WordbookMigration_create"
369 || fqn == "comp_CBlankNode::_create" || fqn == "comp_CURI::_create"
370 || fqn == "comp_CLiteral::_create" || fqn == "CDocumentBuilder::_getInstance"
371 || fqn == "DOM::CDocumentBuilder::_getInstance"
372 || fqn == "xml_security::serial_number_adapter::create"
373 || fqn == "desktop::splash::create" || fqn == "ScannerManager_CreateInstance"
374 || fqn == "formula::FormulaOpCodeMapperObj::create"
375 || fqn == "(anonymous namespace)::createInstance"
376 || fqn == "x_error_handler"
377 || fqn == "warning_func"
378 || fqn == "error_func"
379 || fqn == "ScaDateAddIn_CreateInstance"
380 || fqn == "ScaPricingAddIn_CreateInstance"
381 || fqn == "(anonymous namespace)::PDFSigningPKCS7PasswordCallback"
382 || fqn == "ContextMenuEventLink"
383 || fqn == "DelayedCloseEventLink"
384 || fqn == "GDIMetaFile::ImplColMonoFnc"
385 || fqn == "vcl::getGlyph0"
386 || fqn == "vcl::getGlyph6"
387 || fqn == "vcl::getGlyph12"
388 || fqn == "setPasswordCallback"
389 || fqn == "VCLExceptionSignal_impl"
390 || fqn == "getFontTable"
391 || fqn == "textconversiondlgs::ChineseTranslation_UnoDialog::create"
392 || fqn == "pcr::DefaultHelpProvider::Create"
393 || fqn == "pcr::DefaultFormComponentInspectorModel::Create"
394 || fqn == "pcr::ObjectInspectorModel::Create"
395 || fqn == "GraphicExportFilter::GraphicExportFilter"
396 || fqn == "CertificateContainer::CertificateContainer"
397 || startswith(fqn, "ParseCSS1_")
399 return true;
400 // TODO
401 if (fqn == "FontSubsetInfo::CreateFontSubsetFromType1")
402 return true;
403 // used in template magic
404 if (fqn == "MtfRenderer::MtfRenderer" || fqn == "shell::sessioninstall::SyncDbusSessionHelper::SyncDbusSessionHelper"
405 || fqn == "dp_gui::LicenseDialog::LicenseDialog"
406 || fqn == "(anonymous namespace)::OGLTransitionFactoryImpl::OGLTransitionFactoryImpl")
407 return true;
408 // FIXME
409 if (fqn == "GtkSalDisplay::filterGdkEvent" || fqn == "SvXMLEmbeddedObjectHelper::ImplReadObject"
410 || fqn == "chart::CachedDataSequence::CachedDataSequence")
411 return true;
412 // used via macro
413 if (fqn == "framework::MediaTypeDetectionHelper::MediaTypeDetectionHelper"
414 || fqn == "framework::UriAbbreviation::UriAbbreviation"
415 || fqn == "framework::DispatchDisabler::DispatchDisabler"
416 || fqn == "framework::DispatchRecorderSupplier::DispatchRecorderSupplier")
417 return true;
418 // TODO Armin Le Grand is still working on this
419 if (fqn == "svx::frame::CreateDiagFrameBorderPrimitives"
420 || fqn == "svx::frame::CreateBorderPrimitives")
421 return true;
422 // marked with a TODO
423 if (fqn == "pcr::FormLinkDialog::getExistingRelation"
424 || fqn == "ooo::vba::DebugHelper::basicexception"
425 || fqn == "ScPrintFunc::DrawToDev")
426 return true;
427 // macros at work
428 if (fqn == "msfilter::lcl_PrintDigest")
429 return true;
430 // TODO something wrong here, the method that calls this (Normal::GenSlidingWindowFunction) cannot be correct
431 if (fqn == "sc::opencl::OpBase::Gen")
432 return true;
433 // Can't change this without conflicting with another constructor with the same signature
434 if (fqn == "XclExpSupbook::XclExpSupbook")
435 return true;
436 // ignore the LINK macros from include/tools/link.hxx
437 if (decl->getLocation().isMacroID())
438 return true;
439 // debug code in sw/
440 if (fqn == "lcl_dbg_out")
441 return true;
443 for( auto it = decl->param_begin(); it != decl->param_end(); ++it) {
444 auto param = *it;
445 if (param->hasAttr<UnusedAttr>())
446 continue;
447 if (!param->getName().empty())
448 continue;
449 // ignore params which are enum types with only a single enumerator, these are marker/tag types
450 auto paramType = param->getType();
451 if (paramType->isEnumeralType()) {
452 auto enumType = paramType->getAs<EnumType>();
453 int cnt = std::distance(enumType->getDecl()->enumerator_begin(), enumType->getDecl()->enumerator_end());
454 if (cnt == 1)
455 continue;
457 // ignore params which are a reference to a struct which has no fields.
458 // These are either
459 // (a) marker/tag types
460 // (b) selective "friend" access
461 if (paramType->isReferenceType()) {
462 auto referenceType = paramType->getAs<ReferenceType>();
463 if (referenceType->getPointeeType()->isRecordType()) {
464 auto recordType = referenceType->getPointeeType()->getAs<RecordType>();
465 if (noFieldsInRecord(recordType) == 0)
466 continue;
469 else if (paramType->isRecordType()) {
470 if (noFieldsInRecord(paramType->getAs<RecordType>()) == 0)
471 continue;
473 report( DiagnosticsEngine::Warning,
474 "unused param %0 in %1", compat::getBeginLoc(param))
475 << param->getSourceRange()
476 << param->getName()
477 << fqn;
478 if (canon != decl)
480 unsigned idx = param->getFunctionScopeIndex();
481 const ParmVarDecl* pOther = canon->getParamDecl(idx);
482 report( DiagnosticsEngine::Note, "declaration is here",
483 compat::getBeginLoc(pOther))
484 << pOther->getSourceRange();
487 return true;
490 loplugin::Plugin::Registration<CheckUnusedParams> X("checkunusedparams", false);
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */