Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / checkunusedparams.cxx
blob31dae1c66e613ffa2bb6b9fce985c9520b826c82
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 RecursiveASTVisitor<CheckUnusedParams>, public loplugin::Plugin {
28 public:
29 explicit CheckUnusedParams(loplugin::InstantiationData const & data):
30 Plugin(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( compiler.getSourceManager().getFileEntryForID(
48 compiler.getSourceManager().getMainFileID())->getName() );
49 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/"))
50 return;
51 // Taking pointer to function
52 if (loplugin::isSamePathname(fn, SRCDIR "/l10ntools/source/xmlparse.cxx"))
53 return;
54 // macro magic which declares something needed by an external library
55 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/misc/gridprinter.cxx"))
56 return;
58 // valid test/qa code
59 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/compilerplugins/clang/test/"))
60 return;
61 if (loplugin::isSamePathname(fn, SRCDIR "/cppu/qa/test_reference.cxx"))
62 return;
64 // leave this alone for now
65 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit/"))
66 return;
67 // this has a certain pattern to its code which appears to include lots of unused params
68 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/xmloff/"))
69 return;
70 // I believe someone is busy working on this chunk of code
71 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/dataprovider.cxx"))
72 return;
73 // I think erack is working on stuff here
74 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/excel/xiformula.cxx"))
75 return;
76 // lots of callbacks here
77 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/lotus/op.cxx"))
78 return;
79 // template magic
80 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/html/htmlpars.cxx"))
81 return;
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)
91 return true;
92 checkForFunctionDecl(op->getSubExpr());
93 return true;
96 bool CheckUnusedParams::VisitBinAssign(BinaryOperator const * binaryOperator) {
97 if (m_phase != PluginPhase::FindAddressOf)
98 return true;
99 checkForFunctionDecl(binaryOperator->getRHS());
100 return true;
103 bool CheckUnusedParams::VisitCallExpr(CallExpr const * callExpr) {
104 if (m_phase != PluginPhase::FindAddressOf)
105 return true;
106 for (auto arg : callExpr->arguments())
107 checkForFunctionDecl(arg);
108 return true;
111 bool CheckUnusedParams::VisitCXXConstructExpr(CXXConstructExpr const * constructExpr) {
112 if (m_phase != PluginPhase::FindAddressOf)
113 return true;
114 for (auto arg : constructExpr->arguments())
115 checkForFunctionDecl(arg);
116 return true;
119 bool CheckUnusedParams::VisitInitListExpr(InitListExpr const * initListExpr) {
120 if (m_phase != PluginPhase::FindAddressOf)
121 return true;
122 for (auto subStmt : *initListExpr)
123 checkForFunctionDecl(dyn_cast<Expr>(subStmt));
124 return true;
127 void CheckUnusedParams::checkForFunctionDecl(Expr const * expr, bool bCheckOnly) {
128 auto e1 = expr->IgnoreParenCasts();
129 auto declRef = dyn_cast<DeclRefExpr>(e1);
130 if (!declRef)
131 return;
132 auto functionDecl = dyn_cast<FunctionDecl>(declRef->getDecl());
133 if (!functionDecl)
134 return;
135 if (bCheckOnly)
136 getParentStmt(expr)->dump();
137 else
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))
145 return 1;
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())
154 return false;
155 return rStr.compare(rStr.size() - len, rStr.size(), pSubStr) == 0;
158 bool CheckUnusedParams::VisitFunctionDecl(FunctionDecl const * decl) {
159 if (m_phase != PluginPhase::Warning)
160 return true;
161 if (m_addressOfSet.find(decl->getCanonicalDecl()) != m_addressOfSet.end())
162 return true;
163 if (ignoreLocation(decl))
164 return true;
165 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(decl->getLocation())))
166 return true;
168 auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(decl);
169 if (cxxMethodDecl) {
170 if (cxxMethodDecl->isVirtual())
171 return true;
172 auto cxxConstructorDecl = dyn_cast<CXXConstructorDecl>(cxxMethodDecl);
173 if (cxxConstructorDecl && cxxConstructorDecl->isCopyOrMoveConstructor())
174 return true;
176 if (!decl->isThisDeclarationADefinition())
177 return true;
178 if (decl->isFunctionTemplateSpecialization())
179 return true;
180 if (decl->isDeleted())
181 return true;
182 if (decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate)
183 return true;
184 if (decl->isOverloadedOperator())
185 return true;
186 if (decl->isExternC())
187 return true;
189 //TODO, filtering out any functions relating to class templates for now:
190 CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
191 if (r != nullptr
192 && (r->getTemplateSpecializationKind() != TSK_Undeclared
193 || r->isDependentContext()))
195 return true;
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))
200 return true;
201 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(canon->getLocation())))
202 return true;
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"))
207 return true;
208 // Platform-specific code
209 if (loplugin::isSamePathname(fn, SRCDIR "/include/svl/svdde.hxx"))
210 return true;
211 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/svmain.hxx"))
212 return true;
213 // passing pointer to function
214 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/bitmapaccess.hxx"))
215 return true;
216 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtkobject.hxx"))
217 return true;
218 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtksalframe.hxx"))
219 return true;
220 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/unx/gtk/gtkframe.hxx"))
221 return true;
222 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/gtk/fpicker/SalGtkFilePicker.hxx"))
223 return true;
224 if (loplugin::isSamePathname(fn, SRCDIR "/extensions/source/propctrlr/propertyeditor.hxx"))
225 return true;
226 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/solar/inc/navtoolbar.hxx"))
227 return true;
228 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/grammar.cxx"))
229 return true;
230 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/lexer.cxx"))
231 return true;
232 // marked with a TODO/FIXME
233 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/inc/sallayout.hxx"))
234 return true;
235 if (loplugin::isSamePathname(fn, SRCDIR "/accessibility/inc/standard/vclxaccessiblelist.hxx"))
236 return true;
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"))
239 return true;
240 // these all follow the same pattern, seems a pity to break that
241 if (loplugin::isSamePathname(fn, SRCDIR "/include/vcl/graphicfilter.hxx"))
242 return true;
243 // looks like work in progress
244 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument.cxx"))
245 return true;
246 // macro magic
247 if (loplugin::isSamePathname(fn, SRCDIR "/basctl/source/inc/basidesh.hxx"))
248 return true;
249 // template magic
250 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/canvas/"))
251 return true;
252 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/canvas/"))
253 return true;
254 if (loplugin::isSamePathname(fn, SRCDIR "/include/comphelper/unwrapargs.hxx"))
255 return true;
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"))
258 return true;
259 if (loplugin::isSamePathname(fn, SRCDIR "/include/svx/EnhancedCustomShapeFunctionParser.hxx"))
260 return true;
261 // TODO marker parameter in constructor, should probably be using an enum
262 if (loplugin::isSamePathname(fn, SRCDIR "/framework/inc/uielement/uicommanddescription.hxx"))
263 return true;
264 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/SlideTransitionPane.hxx"))
265 return true;
266 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/animations/CustomAnimationPane.hxx"))
267 return true;
268 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/table/TableDesignPane.hxx"))
269 return true;
270 // debug stuff
271 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/core/data/column2.cxx"))
272 return true;
273 // weird stuff
274 if (loplugin::isSamePathname(fn, SRCDIR "/scaddins/source/analysis/analysishelper.hxx"))
275 return true;
276 // SFX_DECL_CHILDWINDOWCONTEXT macro stuff
277 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/NavigatorChildWindow.hxx"))
278 return true;
279 // TODO, need to remove this from the .sdi file too
280 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/SlideSorterViewShell.hxx"))
281 return true;
282 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/OutlineViewShell.hxx"))
283 return true;
284 // SFX_DECL_INTERFACE macro stuff
285 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/inc/ViewShellBase.hxx"))
286 return true;
287 // debug stuff
288 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/ppt/pptinanimations.hxx"))
289 return true;
290 // takes pointer to fn
291 if (loplugin::isSamePathname(fn, SRCDIR "/include/sfx2/shell.hxx"))
292 return true;
293 // TODO, need to remove this from the .sdi file too
294 if (fqn == "SfxObjectShell::StateView_Impl")
295 return true;
296 // SFX_DECL_CHILDWINDOW_WITHID macro
297 if (loplugin::isSamePathname(fn, SRCDIR "/include/sfx2/infobar.hxx"))
298 return true;
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"))
301 return true;
302 // SFX_DECL_VIEWFACTORY macro
303 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/inc/view.hxx"))
304 return true;
305 // debugging
306 if (fqn == "BrowseBox::DoShowCursor" || fqn == "BrowseBox::DoHideCursor")
307 return true;
308 // if I change this one, it then overrides a superclass virtual method
309 if (fqn == "GalleryBrowser2::KeyInput")
310 return true;
311 // takes pointer to function
312 if (fqn == "cmis::AuthProvider::onedriveAuthCodeFallback" || fqn == "cmis::AuthProvider::gdriveAuthCodeFallback")
313 return true;
314 if (fqn == "ooo_mount_operation_ask_password")
315 return true;
316 // TODO tricky to remove because of default params
317 if (fqn == "xmloff::OAttribute2Property::addBooleanProperty")
318 return true;
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_"))
328 return true;
329 // WIN32 only
330 if (fqn == "SwFntObj::GuessLeading")
331 return true;
332 // SFX_DECL_CHILDWINDOW_WITHID macro
333 if (fqn == "SwSpellDialogChildWindow::SwSpellDialogChildWindow"
334 || fqn == "SwFieldDlgWrapper::SwFieldDlgWrapper"
335 || fqn == "SwInputChild::SwInputChild")
336 return true;
337 // SFX_DECL_VIEWFACTORY macro
338 if (fqn == "SwSrcView::SwSrcView")
339 return true;
340 // Serves to disambiguate two very similar methods
341 if (fqn == "MSWordStyles::BuildGetSlot")
342 return true;
343 // TODO there are just too many default params to make this worth fixing right now
344 if (fqn == "ScDocument::CopyMultiRangeFromClip")
345 return true;
346 // TODO looks like this needs fixing?
347 if (fqn == "ScTable::ExtendPrintArea")
348 return true;
349 // there is a FIXME in the code
350 if (fqn == "ScRangeUtil::IsAbsTabArea")
351 return true;
352 // SFX_DECL_CHILDWINDOW_WITHID
353 if (fqn == "ScInputWindowWrapper::ScInputWindowWrapper"
354 || fqn == "sc::SearchResultsDlgWrapper::SearchResultsDlgWrapper")
355 return true;
356 // ExecMethod in .sdi file
357 if (fqn == "ScChartShell::ExecuteExportAsGraphic")
358 return true;
359 // bool marker parameter
360 if (fqn == "SvxIconReplacementDialog::SvxIconReplacementDialog")
361 return true;
362 // used as pointer to fn
363 if (endswith(fqn, "_createInstance"))
364 return true;
365 // callback
366 if (startswith(fqn, "SbRtl_"))
367 return true;
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_")
400 return true;
401 // TODO
402 if (fqn == "FontSubsetInfo::CreateFontSubsetFromType1")
403 return true;
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")
408 return true;
409 // FIXME
410 if (fqn == "GtkSalDisplay::filterGdkEvent" || fqn == "SvXMLEmbeddedObjectHelper::ImplReadObject"
411 || fqn == "chart::CachedDataSequence::CachedDataSequence")
412 return true;
413 // used via macro
414 if (fqn == "framework::MediaTypeDetectionHelper::MediaTypeDetectionHelper"
415 || fqn == "framework::UriAbbreviation::UriAbbreviation"
416 || fqn == "framework::DispatchDisabler::DispatchDisabler"
417 || fqn == "framework::DispatchRecorderSupplier::DispatchRecorderSupplier")
418 return true;
419 // TODO Armin Le Grand is still working on this
420 if (fqn == "svx::frame::CreateDiagFrameBorderPrimitives"
421 || fqn == "svx::frame::CreateBorderPrimitives")
422 return true;
423 // marked with a TODO
424 if (fqn == "pcr::FormLinkDialog::getExistingRelation"
425 || fqn == "ooo::vba::DebugHelper::basicexception"
426 || fqn == "ScPrintFunc::DrawToDev")
427 return true;
428 // macros at work
429 if (fqn == "msfilter::lcl_PrintDigest")
430 return true;
431 // TODO something wrong here, the method that calls this (Normal::GenSlidingWindowFunction) cannot be correct
432 if (fqn == "sc::opencl::OpBase::Gen")
433 return true;
434 // Can't change this without conflicting with another constructor with the same signature
435 if (fqn == "XclExpSupbook::XclExpSupbook")
436 return true;
437 // ignore the LINK macros from include/tools/link.hxx
438 if (decl->getLocation().isMacroID())
439 return true;
440 // debug code in sw/
441 if (fqn == "lcl_dbg_out")
442 return true;
444 for( auto it = decl->param_begin(); it != decl->param_end(); ++it) {
445 auto param = *it;
446 if (param->hasAttr<UnusedAttr>())
447 continue;
448 if (!param->getName().empty())
449 continue;
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());
455 if (cnt == 1)
456 continue;
458 // ignore params which are a reference to a struct which has no fields.
459 // These are either
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)
467 continue;
470 else if (paramType->isRecordType()) {
471 if (noFieldsInRecord(paramType->getAs<RecordType>()) == 0)
472 continue;
474 report( DiagnosticsEngine::Warning,
475 "unused param %0 in %1", param->getLocStart())
476 << param->getSourceRange()
477 << param->getName()
478 << fqn;
479 if (canon != decl)
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();
488 return true;
491 loplugin::Plugin::Registration<CheckUnusedParams> X("checkunusedparams", false);
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */