1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include <config_global.h>
16 #include <unordered_set>
17 #include <unordered_map>
22 This is a compile check. The results of this plugin need to be checked by hand, since it is a collection of heuristics.
24 Check for unused variable where
25 (*) we never call methods that return information from the variable.
26 (*) we never pass the variable to anything else
28 Classes which are safe to be warned about need to be marked using
29 SAL_WARN_UNUSED (see e.g. OUString). For external classes such as std::vector
30 that cannot be edited there is a manual list.
32 This is an expensive plugin, since it walks up the parent tree,
33 so it is off by default.
36 class UnusedVariableMore
: public loplugin::FilteringPlugin
<UnusedVariableMore
>
39 explicit UnusedVariableMore(const InstantiationData
& data
);
40 virtual void run() override
;
41 bool VisitVarDecl(VarDecl
const*);
42 bool VisitDeclRefExpr(DeclRefExpr
const*);
43 bool VisitFunctionDecl(FunctionDecl
const*);
46 bool checkifUnused(Stmt
const*, VarDecl
const*);
47 bool isOkForParameter(const QualType
&);
49 std::unordered_set
<VarDecl
const*> interestingSet
;
50 // used to dump the last place that said the usage was unused, for debug purposes
51 std::unordered_map
<VarDecl
const*, Stmt
const*> interestingDebugMap
;
54 UnusedVariableMore::UnusedVariableMore(const InstantiationData
& data
)
55 : FilteringPlugin(data
)
59 void UnusedVariableMore::run()
61 std::string
fn(handler
.getMainFileName());
62 loplugin::normalizeDotDotInFilePath(fn
);
65 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sal/qa/"))
67 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/i18npool/qa/"))
69 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/qa/"))
72 // vector of shared_ptr used to delay destruction
73 if (fn
== SRCDIR
"/cppuhelper/source/servicemanager.cxx")
75 if (fn
== SRCDIR
"/i18nlangtag/source/languagetag/languagetag.cxx")
77 if (fn
== SRCDIR
"/vcl/workben/outdevgrind.cxx")
79 // unordered_set of Reference to delay destruction
80 if (fn
== SRCDIR
"/stoc/source/servicemanager/servicemanager.cxx")
82 // TODO "operator >>" fooling me here
83 if (fn
== SRCDIR
"/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
86 if (fn
== SRCDIR
"/sfx2/source/dialog/dinfdlg.cxx")
88 // std::vector< Reference< XInterface > > keep alive
89 if (fn
== SRCDIR
"/dbaccess/source/core/dataaccess/databasedocument.cxx")
92 if (fn
== SRCDIR
"/sc/source/core/tool/scmatrix.cxx")
94 // storing local copy of Link<>
95 if (fn
== SRCDIR
"/sc/source/ui/miscdlgs/simpref.cxx")
97 // Using an SwPaM to do stuff
98 if (fn
== SRCDIR
"/sw/source/core/crsr/bookmrk.cxx")
100 // index variable in for loop?
101 if (fn
== SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx")
103 // TODO "operator >>" fooling me here
104 if (fn
== SRCDIR
"/sw/source/filter/ww8/ww8par.cxx")
106 // TODO "operator >>" fooling me here
107 if (fn
== SRCDIR
"/sc/source/filter/excel/xistream.cxx")
110 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
112 for (VarDecl
const* varDecl
: interestingSet
)
114 report(DiagnosticsEngine::Warning
, "unused variable %0", varDecl
->getLocation())
115 << varDecl
->getDeclName();
116 //auto it = interestingDebugMap.find(varDecl);
117 //if (it != interestingDebugMap.end())
118 // it->second->dump();
122 bool isWarnUnusedType(QualType type
)
124 if (auto const t
= type
->getAs
<TypedefType
>())
126 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
131 if (auto const t
= type
->getAs
<RecordType
>())
133 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
138 return loplugin::isExtraWarnUnusedType(type
);
141 bool UnusedVariableMore::VisitVarDecl(VarDecl
const* var
)
143 if (ignoreLocation(var
))
145 if (var
->isDefinedOutsideFunctionOrMethod())
147 if (isa
<ParmVarDecl
>(var
))
149 if (!isWarnUnusedType(var
->getType()))
153 auto dc
= loplugin::TypeCheck(var
->getType());
154 if (dc
.Class("ZCodec").GlobalNamespace())
156 if (dc
.Class("ScopedVclPtrInstance").GlobalNamespace())
158 if (dc
.Class("VclPtrInstance").GlobalNamespace())
160 if (dc
.Class("Config").GlobalNamespace())
162 // I think these classes modify global state somehow
163 if (dc
.Class("SvtHistoryOptions").GlobalNamespace())
165 if (dc
.Class("SvtSecurityOptions").GlobalNamespace())
167 if (dc
.Class("SvtViewOptions").GlobalNamespace())
169 if (dc
.Class("SvtUserOptions").GlobalNamespace())
171 if (dc
.Class("SvtFontOptions").GlobalNamespace())
173 if (dc
.Class("SvtMenuOptions").GlobalNamespace())
175 if (dc
.Class("SvtPathOptions").GlobalNamespace())
177 if (dc
.Class("SvtPrintWarningOptions").GlobalNamespace())
179 if (dc
.Class("SvtSysLocaleOptions").GlobalNamespace())
182 interestingSet
.insert(var
);
186 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr
const* declRefExpr
)
188 if (ignoreLocation(declRefExpr
))
190 auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
193 if (interestingSet
.find(varDecl
) == interestingSet
.end())
195 if (!checkifUnused(declRefExpr
, varDecl
))
196 interestingSet
.erase(varDecl
);
200 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
201 // related VarDecl is unused.
202 bool UnusedVariableMore::checkifUnused(Stmt
const* stmt
, VarDecl
const* varDecl
)
204 const Stmt
* parent
= getParentStmt(stmt
);
207 // check if we're inside a CXXCtorInitializer
208 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
209 if (parentsRange
.begin() != parentsRange
.end())
211 auto parentDecl
= parentsRange
.begin()->get
<Decl
>();
212 if (parentDecl
&& (isa
<CXXConstructorDecl
>(parentDecl
) || isa
<VarDecl
>(parentDecl
)))
215 interestingDebugMap
[varDecl
] = stmt
;
219 if (isa
<ReturnStmt
>(parent
))
221 if (isa
<IfStmt
>(parent
))
223 if (isa
<SwitchStmt
>(parent
))
225 if (isa
<InitListExpr
>(parent
))
227 if (isa
<CXXConstructExpr
>(parent
))
229 if (isa
<BinaryOperator
>(parent
))
231 if (isa
<UnaryOperator
>(parent
))
233 if (isa
<ConditionalOperator
>(parent
))
235 if (isa
<ArraySubscriptExpr
>(parent
))
237 if (isa
<CXXBindTemporaryExpr
>(parent
))
238 return checkifUnused(parent
, varDecl
);
239 if (isa
<MaterializeTemporaryExpr
>(parent
))
240 return checkifUnused(parent
, varDecl
);
242 if (isa
<CompoundStmt
>(parent
))
244 interestingDebugMap
[varDecl
] = parent
;
248 // check for cast to void
249 if (auto explicitCastExpr
= dyn_cast
<ExplicitCastExpr
>(parent
))
251 if (loplugin::TypeCheck(explicitCastExpr
->getTypeAsWritten()).Void())
255 if (isa
<MemberExpr
>(parent
))
256 return checkifUnused(parent
, varDecl
);
257 if (isa
<ExprWithCleanups
>(parent
))
258 return checkifUnused(parent
, varDecl
);
259 if (isa
<CastExpr
>(parent
))
260 return checkifUnused(parent
, varDecl
);
261 if (isa
<ParenExpr
>(parent
))
262 return checkifUnused(parent
, varDecl
);
264 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
266 const CXXMethodDecl
* calleeMethodDecl
267 = dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
268 if (calleeMethodDecl
)
270 if (calleeMethodDecl
->getNumParams() == 0)
271 return checkifUnused(parent
, varDecl
);
272 if (operatorCallExpr
->getArg(0) == stmt
)
273 return checkifUnused(parent
, varDecl
);
276 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
278 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>(stmt
);
279 if (memberExpr
&& memberCallExpr
->getImplicitObjectArgument() == memberExpr
->getBase())
281 // if we are calling a method on the varDecl, walk up
282 if (!checkifUnused(parent
, varDecl
))
284 // check if we are passing something to the var by non-const ref, in which case it is updating something else for us
285 const FunctionDecl
* calleeFunctionDecl
= memberCallExpr
->getDirectCallee();
286 if (calleeFunctionDecl
)
288 if (calleeFunctionDecl
->isVariadic())
290 for (unsigned i
= 0; i
< memberCallExpr
->getNumArgs(); ++i
)
292 if (i
>= calleeFunctionDecl
->getNumParams()) // can happen in template code
294 interestingDebugMap
[varDecl
] = parent
;
297 if (!isOkForParameter(calleeFunctionDecl
->getParamDecl(i
)->getType()))
301 interestingDebugMap
[varDecl
] = parent
;
305 if (isa
<CallExpr
>(parent
))
308 interestingDebugMap
[varDecl
] = parent
;
312 bool UnusedVariableMore::isOkForParameter(const QualType
& qt
)
314 if (qt
->isIntegralOrEnumerationType())
316 auto const type
= loplugin::TypeCheck(qt
);
319 return bool(type
.Pointer().Const());
321 else if (type
.LvalueReference())
323 return bool(type
.LvalueReference().Const());
328 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
330 if (ignoreLocation(functionDecl
))
332 //functionDecl->dump();
336 static Plugin::Registration
<UnusedVariableMore
> X("unusedvariablemore", false);
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */