1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
27 class CheckUnusedParams
: public RecursiveASTVisitor
<CheckUnusedParams
>, public loplugin::Plugin
{
29 explicit CheckUnusedParams(loplugin::InstantiationData
const & data
):
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 *);
39 void checkForFunctionDecl(Expr
const *, bool bCheckOnly
= false);
40 std::set
<FunctionDecl
const *> m_addressOfSet
;
41 enum class PluginPhase
{ FindAddressOf
, Warning
};
45 void CheckUnusedParams::run()
47 StringRef
fn( compiler
.getSourceManager().getFileEntryForID(
48 compiler
.getSourceManager().getMainFileID())->getName() );
49 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sal/"))
51 // Taking pointer to function
52 if (loplugin::isSamePathname(fn
, SRCDIR
"/l10ntools/source/xmlparse.cxx"))
54 // macro magic which declares something needed by an external library
55 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/misc/gridprinter.cxx"))
59 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/compilerplugins/clang/test/"))
61 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppu/qa/test_reference.cxx"))
64 // leave this alone for now
65 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/libreofficekit/"))
67 // this has a certain pattern to its code which appears to include lots of unused params
68 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/xmloff/"))
70 // I believe someone is busy working on this chunk of code
71 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/dataprovider.cxx"))
73 // I think erack is working on stuff here
74 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/excel/xiformula.cxx"))
76 // lots of callbacks here
77 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/lotus/op.cxx"))
80 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/html/htmlpars.cxx"))
83 m_phase
= PluginPhase::FindAddressOf
;
84 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
85 m_phase
= PluginPhase::Warning
;
86 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
89 bool CheckUnusedParams::VisitUnaryAddrOf(UnaryOperator
const * op
) {
90 if (m_phase
!= PluginPhase::FindAddressOf
)
92 checkForFunctionDecl(op
->getSubExpr());
96 bool CheckUnusedParams::VisitBinAssign(BinaryOperator
const * binaryOperator
) {
97 if (m_phase
!= PluginPhase::FindAddressOf
)
99 checkForFunctionDecl(binaryOperator
->getRHS());
103 bool CheckUnusedParams::VisitCallExpr(CallExpr
const * callExpr
) {
104 if (m_phase
!= PluginPhase::FindAddressOf
)
106 for (auto arg
: callExpr
->arguments())
107 checkForFunctionDecl(arg
);
111 bool CheckUnusedParams::VisitCXXConstructExpr(CXXConstructExpr
const * constructExpr
) {
112 if (m_phase
!= PluginPhase::FindAddressOf
)
114 for (auto arg
: constructExpr
->arguments())
115 checkForFunctionDecl(arg
);
119 bool CheckUnusedParams::VisitInitListExpr(InitListExpr
const * initListExpr
) {
120 if (m_phase
!= PluginPhase::FindAddressOf
)
122 for (auto subStmt
: *initListExpr
)
123 checkForFunctionDecl(dyn_cast
<Expr
>(subStmt
));
127 void CheckUnusedParams::checkForFunctionDecl(Expr
const * expr
, bool bCheckOnly
) {
128 auto e1
= expr
->IgnoreParenCasts();
129 auto declRef
= dyn_cast
<DeclRefExpr
>(e1
);
132 auto functionDecl
= dyn_cast
<FunctionDecl
>(declRef
->getDecl());
136 getParentStmt(expr
)->dump();
138 m_addressOfSet
.insert(functionDecl
->getCanonicalDecl());
141 static int noFieldsInRecord(RecordType
const * recordType
) {
142 auto recordDecl
= recordType
->getDecl();
143 // if it's complicated, lets just assume it has fields
144 if (isa
<ClassTemplateSpecializationDecl
>(recordDecl
))
146 return std::distance(recordDecl
->field_begin(), recordDecl
->field_end());
148 static bool startswith(const std::string
& rStr
, const char* pSubStr
) {
149 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
151 static bool endswith(const std::string
& rStr
, const char* pSubStr
) {
152 auto len
= strlen(pSubStr
);
153 if (len
> rStr
.size())
155 return rStr
.compare(rStr
.size() - len
, rStr
.size(), pSubStr
) == 0;
158 bool CheckUnusedParams::VisitFunctionDecl(FunctionDecl
const * decl
) {
159 if (m_phase
!= PluginPhase::Warning
)
161 if (m_addressOfSet
.find(decl
->getCanonicalDecl()) != m_addressOfSet
.end())
163 if (ignoreLocation(decl
))
165 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(decl
->getLocation())))
168 auto cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(decl
);
170 if (cxxMethodDecl
->isVirtual())
172 auto cxxConstructorDecl
= dyn_cast
<CXXConstructorDecl
>(cxxMethodDecl
);
173 if (cxxConstructorDecl
&& cxxConstructorDecl
->isCopyOrMoveConstructor())
176 if (!decl
->isThisDeclarationADefinition())
178 if (decl
->isFunctionTemplateSpecialization())
180 if (decl
->isDeleted())
182 if (decl
->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate
)
184 if (decl
->isOverloadedOperator())
186 if (decl
->isExternC())
189 //TODO, filtering out any functions relating to class templates for now:
190 CXXRecordDecl
const * r
= dyn_cast
<CXXRecordDecl
>(decl
->getDeclContext());
192 && (r
->getTemplateSpecializationKind() != TSK_Undeclared
193 || r
->isDependentContext()))
197 FunctionDecl
const * canon
= decl
->getCanonicalDecl();
198 std::string fqn
= canon
->getQualifiedNameAsString(); // because sometimes clang returns nonsense for the filename of canon
199 if (ignoreLocation(canon
))
201 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(canon
->getLocation())))
203 StringRef fn
= compiler
.getSourceManager().getFilename(compiler
.getSourceManager().getSpellingLoc(canon
->getLocStart()));
204 // Some backwards compat magic.
205 // TODO Can probably be removed, but need to do some checking
206 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/sax/fshelper.hxx"))
208 // Platform-specific code
209 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/svl/svdde.hxx"))
211 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/vcl/svmain.hxx"))
213 // passing pointer to function
214 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/vcl/bitmapaccess.hxx"))
216 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/inc/unx/gtk/gtkobject.hxx"))
218 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/inc/unx/gtk/gtksalframe.hxx"))
220 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/inc/unx/gtk/gtkframe.hxx"))
222 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx"))
224 if (loplugin::isSamePathname(fn
, SRCDIR
"/extensions/source/propctrlr/propertyeditor.hxx"))
226 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/solar/inc/navtoolbar.hxx"))
228 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/grammar.cxx"))
230 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/lexer.cxx"))
232 // marked with a TODO/FIXME
233 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/inc/sallayout.hxx"))
235 if (loplugin::isSamePathname(fn
, SRCDIR
"/accessibility/inc/standard/vclxaccessiblelist.hxx"))
237 // these are "extern C" but clang doesn't seem to report that accurately
238 if (loplugin::isSamePathname(fn
, SRCDIR
"/sax/source/fastparser/fastparser.cxx"))
240 // these all follow the same pattern, seems a pity to break that
241 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/vcl/graphicfilter.hxx"))
243 // looks like work in progress
244 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument.cxx"))
247 if (loplugin::isSamePathname(fn
, SRCDIR
"/basctl/source/inc/basidesh.hxx"))
250 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/canvas/"))
252 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/include/canvas/"))
254 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/comphelper/unwrapargs.hxx"))
256 // this looks like vaguely useful code (ParseError) that I'm loathe to remove
257 if (loplugin::isSamePathname(fn
, SRCDIR
"/connectivity/source/inc/RowFunctionParser.hxx"))
259 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/svx/EnhancedCustomShapeFunctionParser.hxx"))
261 // TODO marker parameter in constructor, should probably be using an enum
262 if (loplugin::isSamePathname(fn
, SRCDIR
"/framework/inc/uielement/uicommanddescription.hxx"))
264 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/inc/SlideTransitionPane.hxx"))
266 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/animations/CustomAnimationPane.hxx"))
268 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/table/TableDesignPane.hxx"))
271 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/core/data/column2.cxx"))
274 if (loplugin::isSamePathname(fn
, SRCDIR
"/scaddins/source/analysis/analysishelper.hxx"))
276 // SFX_DECL_CHILDWINDOWCONTEXT macro stuff
277 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/inc/NavigatorChildWindow.hxx"))
279 // TODO, need to remove this from the .sdi file too
280 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/inc/SlideSorterViewShell.hxx"))
282 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/inc/OutlineViewShell.hxx"))
284 // SFX_DECL_INTERFACE macro stuff
285 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/inc/ViewShellBase.hxx"))
288 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/ppt/pptinanimations.hxx"))
290 // takes pointer to fn
291 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/sfx2/shell.hxx"))
293 // TODO, need to remove this from the .sdi file too
294 if (fqn
== "SfxObjectShell::StateView_Impl")
296 // SFX_DECL_CHILDWINDOW_WITHID macro
297 if (loplugin::isSamePathname(fn
, SRCDIR
"/include/sfx2/infobar.hxx"))
299 // this looks like vaguely useful code (ParseError) that I'm loathe to remove
300 if (loplugin::isSamePathname(fn
, SRCDIR
"/slideshow/source/inc/slideshowexceptions.hxx"))
302 // SFX_DECL_VIEWFACTORY macro
303 if (loplugin::isSamePathname(fn
, SRCDIR
"/starmath/inc/view.hxx"))
306 if (fqn
== "BrowseBox::DoShowCursor" || fqn
== "BrowseBox::DoHideCursor")
308 // if I change this one, it then overrides a superclass virtual method
309 if (fqn
== "GalleryBrowser2::KeyInput")
311 // takes pointer to function
312 if (fqn
== "cmis::AuthProvider::onedriveAuthCodeFallback" || fqn
== "cmis::AuthProvider::gdriveAuthCodeFallback")
314 if (fqn
== "ooo_mount_operation_ask_password")
316 // TODO tricky to remove because of default params
317 if (fqn
== "xmloff::OAttribute2Property::addBooleanProperty")
319 // taking pointer to function
320 if (fqn
== "sw::DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl"
321 || fqn
== "sw::DocumentContentOperationsManager::DeleteRangeImpl"
322 || fqn
== "SwTableFormula::GetFormulaBoxes"
323 || fqn
== "SwFEShell::Drag"
324 || fqn
== "GetASCWriter" || fqn
== "GetHTMLWriter" || fqn
== "GetXMLWriter"
325 || fqn
== "SwWrtShell::UpdateLayoutFrame" || fqn
== "SwWrtShell::DefaultDrag"
326 || fqn
== "SwWrtShell::DefaultEndDrag"
327 || startswith(fqn
, "SwWW8ImplReader::Read_"))
330 if (fqn
== "SwFntObj::GuessLeading")
332 // SFX_DECL_CHILDWINDOW_WITHID macro
333 if (fqn
== "SwSpellDialogChildWindow::SwSpellDialogChildWindow"
334 || fqn
== "SwFieldDlgWrapper::SwFieldDlgWrapper"
335 || fqn
== "SwInputChild::SwInputChild")
337 // SFX_DECL_VIEWFACTORY macro
338 if (fqn
== "SwSrcView::SwSrcView")
340 // Serves to disambiguate two very similar methods
341 if (fqn
== "MSWordStyles::BuildGetSlot")
343 // TODO there are just too many default params to make this worth fixing right now
344 if (fqn
== "ScDocument::CopyMultiRangeFromClip")
346 // TODO looks like this needs fixing?
347 if (fqn
== "ScTable::ExtendPrintArea")
349 // there is a FIXME in the code
350 if (fqn
== "ScRangeUtil::IsAbsTabArea")
352 // SFX_DECL_CHILDWINDOW_WITHID
353 if (fqn
== "ScInputWindowWrapper::ScInputWindowWrapper"
354 || fqn
== "sc::SearchResultsDlgWrapper::SearchResultsDlgWrapper")
356 // ExecMethod in .sdi file
357 if (fqn
== "ScChartShell::ExecuteExportAsGraphic")
359 // bool marker parameter
360 if (fqn
== "SvxIconReplacementDialog::SvxIconReplacementDialog")
362 // used as pointer to fn
363 if (endswith(fqn
, "_createInstance"))
366 if (startswith(fqn
, "SbRtl_"))
368 // takes pointer to fn
369 if (fqn
== "migration::BasicMigration_create" || fqn
== "migration::WordbookMigration_create"
370 || fqn
== "comp_CBlankNode::_create" || fqn
== "comp_CURI::_create"
371 || fqn
== "comp_CLiteral::_create" || fqn
== "CDocumentBuilder::_getInstance"
372 || fqn
== "DOM::CDocumentBuilder::_getInstance"
373 || fqn
== "xml_security::serial_number_adapter::create"
374 || fqn
== "desktop::splash::create" || fqn
== "ScannerManager_CreateInstance"
375 || fqn
== "formula::FormulaOpCodeMapperObj::create"
376 || fqn
== "(anonymous namespace)::createInstance"
377 || fqn
== "x_error_handler"
378 || fqn
== "warning_func"
379 || fqn
== "error_func"
380 || fqn
== "ScaDateAddIn_CreateInstance"
381 || fqn
== "ScaPricingAddIn_CreateInstance"
382 || fqn
== "(anonymous namespace)::PDFSigningPKCS7PasswordCallback"
383 || fqn
== "ContextMenuEventLink"
384 || fqn
== "DelayedCloseEventLink"
385 || fqn
== "GDIMetaFile::ImplColMonoFnc"
386 || fqn
== "vcl::getGlyph0"
387 || fqn
== "vcl::getGlyph6"
388 || fqn
== "vcl::getGlyph12"
389 || fqn
== "setPasswordCallback"
390 || fqn
== "VCLExceptionSignal_impl"
391 || fqn
== "getFontTable"
392 || fqn
== "textconversiondlgs::ChineseTranslation_UnoDialog::create"
393 || fqn
== "pcr::DefaultHelpProvider::Create"
394 || fqn
== "pcr::DefaultFormComponentInspectorModel::Create"
395 || fqn
== "pcr::ObjectInspectorModel::Create"
396 || fqn
== "GraphicExportFilter::GraphicExportFilter"
397 || fqn
== "CertificateContainer::CertificateContainer"
398 || startswith(fqn
, "ParseCSS1_")
402 if (fqn
== "FontSubsetInfo::CreateFontSubsetFromType1")
404 // used in template magic
405 if (fqn
== "MtfRenderer::MtfRenderer" || fqn
== "shell::sessioninstall::SyncDbusSessionHelper::SyncDbusSessionHelper"
406 || fqn
== "dp_gui::LicenseDialog::LicenseDialog"
407 || fqn
== "(anonymous namespace)::OGLTransitionFactoryImpl::OGLTransitionFactoryImpl")
410 if (fqn
== "GtkSalDisplay::filterGdkEvent" || fqn
== "SvXMLEmbeddedObjectHelper::ImplReadObject"
411 || fqn
== "chart::CachedDataSequence::CachedDataSequence")
414 if (fqn
== "framework::MediaTypeDetectionHelper::MediaTypeDetectionHelper"
415 || fqn
== "framework::UriAbbreviation::UriAbbreviation"
416 || fqn
== "framework::DispatchDisabler::DispatchDisabler"
417 || fqn
== "framework::DispatchRecorderSupplier::DispatchRecorderSupplier")
419 // TODO Armin Le Grand is still working on this
420 if (fqn
== "svx::frame::CreateDiagFrameBorderPrimitives"
421 || fqn
== "svx::frame::CreateBorderPrimitives")
423 // marked with a TODO
424 if (fqn
== "pcr::FormLinkDialog::getExistingRelation"
425 || fqn
== "ooo::vba::DebugHelper::basicexception"
426 || fqn
== "ScPrintFunc::DrawToDev")
429 if (fqn
== "msfilter::lcl_PrintDigest")
431 // TODO something wrong here, the method that calls this (Normal::GenSlidingWindowFunction) cannot be correct
432 if (fqn
== "sc::opencl::OpBase::Gen")
434 // Can't change this without conflicting with another constructor with the same signature
435 if (fqn
== "XclExpSupbook::XclExpSupbook")
437 // ignore the LINK macros from include/tools/link.hxx
438 if (decl
->getLocation().isMacroID())
441 if (fqn
== "lcl_dbg_out")
444 for( auto it
= decl
->param_begin(); it
!= decl
->param_end(); ++it
) {
446 if (param
->hasAttr
<UnusedAttr
>())
448 if (!param
->getName().empty())
450 // ignore params which are enum types with only a single enumerator, these are marker/tag types
451 auto paramType
= param
->getType();
452 if (paramType
->isEnumeralType()) {
453 auto enumType
= paramType
->getAs
<EnumType
>();
454 int cnt
= std::distance(enumType
->getDecl()->enumerator_begin(), enumType
->getDecl()->enumerator_end());
458 // ignore params which are a reference to a struct which has no fields.
460 // (a) marker/tag types
461 // (b) selective "friend" access
462 if (paramType
->isReferenceType()) {
463 auto referenceType
= paramType
->getAs
<ReferenceType
>();
464 if (referenceType
->getPointeeType()->isRecordType()) {
465 auto recordType
= referenceType
->getPointeeType()->getAs
<RecordType
>();
466 if (noFieldsInRecord(recordType
) == 0)
470 else if (paramType
->isRecordType()) {
471 if (noFieldsInRecord(paramType
->getAs
<RecordType
>()) == 0)
474 report( DiagnosticsEngine::Warning
,
475 "unused param %0 in %1", param
->getLocStart())
476 << param
->getSourceRange()
481 unsigned idx
= param
->getFunctionScopeIndex();
482 const ParmVarDecl
* pOther
= canon
->getParamDecl(idx
);
483 report( DiagnosticsEngine::Note
, "declaration is here",
484 pOther
->getLocStart())
485 << pOther
->getSourceRange();
491 loplugin::Plugin::Registration
<CheckUnusedParams
> X("checkunusedparams", false);
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */