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_clang.h"
13 #include <config_global.h>
17 #include <unordered_set>
18 #include <unordered_map>
20 #include "clang/AST/ParentMapContext.h"
25 This is a compile check. The results of this plugin need to be checked by hand, since it is a collection of heuristics.
27 Check for unused variable where
28 (*) we never call methods that return information from the variable.
29 (*) we never pass the variable to anything else
31 Classes which are safe to be warned about need to be marked using
32 SAL_WARN_UNUSED (see e.g. OUString). For external classes such as std::vector
33 that cannot be edited there is a manual list.
35 This is an expensive plugin, since it walks up the parent tree,
36 so it is off by default.
39 class UnusedVariableMore
: public loplugin::FilteringPlugin
<UnusedVariableMore
>
42 explicit UnusedVariableMore(const InstantiationData
& data
);
43 virtual void run() override
;
44 bool VisitVarDecl(VarDecl
const*);
45 bool VisitDeclRefExpr(DeclRefExpr
const*);
46 bool VisitFunctionDecl(FunctionDecl
const*);
49 bool checkifUnused(Stmt
const*, VarDecl
const*);
50 bool isOkForParameter(const QualType
&);
52 std::unordered_set
<VarDecl
const*> interestingSet
;
53 // used to dump the last place that said the usage was unused, for debug purposes
54 std::unordered_map
<VarDecl
const*, Stmt
const*> interestingDebugMap
;
57 UnusedVariableMore::UnusedVariableMore(const InstantiationData
& data
)
58 : FilteringPlugin(data
)
62 void UnusedVariableMore::run()
64 std::string
fn(handler
.getMainFileName());
65 loplugin::normalizeDotDotInFilePath(fn
);
68 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sal/qa/"))
70 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/i18npool/qa/"))
72 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/qa/"))
75 // vector of shared_ptr used to delay destruction
76 if (fn
== SRCDIR
"/cppuhelper/source/servicemanager.cxx")
78 if (fn
== SRCDIR
"/i18nlangtag/source/languagetag/languagetag.cxx")
80 // unordered_set of Reference to delay destruction
81 if (fn
== SRCDIR
"/stoc/source/servicemanager/servicemanager.cxx")
83 // TODO "operator >>" fooling me here
84 if (fn
== SRCDIR
"/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
87 if (fn
== SRCDIR
"/sfx2/source/dialog/dinfdlg.cxx")
89 // std::vector< Reference< XInterface > > keep alive
90 if (fn
== SRCDIR
"/dbaccess/source/core/dataaccess/databasedocument.cxx")
93 if (fn
== SRCDIR
"/sc/source/core/tool/scmatrix.cxx")
95 // storing local copy of Link<>
96 if (fn
== SRCDIR
"/sc/source/ui/miscdlgs/simpref.cxx")
98 // Using an SwPaM to do stuff
99 if (fn
== SRCDIR
"/sw/source/core/crsr/bookmark.cxx")
101 // index variable in for loop?
102 if (fn
== SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx")
104 // TODO "operator >>" fooling me here
105 if (fn
== SRCDIR
"/sw/source/filter/ww8/ww8par.cxx")
107 // TODO "operator >>" fooling me here
108 if (fn
== SRCDIR
"/sc/source/filter/excel/xistream.cxx")
111 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
113 for (VarDecl
const* varDecl
: interestingSet
)
115 report(DiagnosticsEngine::Warning
, "unused variable %0", varDecl
->getLocation())
116 << varDecl
->getDeclName();
117 //auto it = interestingDebugMap.find(varDecl);
118 //if (it != interestingDebugMap.end())
119 // it->second->dump();
123 bool isWarnUnusedType(QualType type
)
125 if (auto const t
= type
->getAs
<TypedefType
>())
127 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
132 if (auto const t
= type
->getAs
<RecordType
>())
134 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
139 return loplugin::isExtraWarnUnusedType(type
);
142 bool UnusedVariableMore::VisitVarDecl(VarDecl
const* var
)
144 if (ignoreLocation(var
))
146 if (var
->isDefinedOutsideFunctionOrMethod())
148 if (isa
<ParmVarDecl
>(var
))
150 if (!isWarnUnusedType(var
->getType()))
154 auto dc
= loplugin::TypeCheck(var
->getType());
155 if (dc
.Class("ZCodec").GlobalNamespace())
157 if (dc
.Class("ScopedVclPtrInstance").GlobalNamespace())
159 if (dc
.Class("VclPtrInstance").GlobalNamespace())
161 if (dc
.Class("Config").GlobalNamespace())
163 // I think these classes modify global state somehow
164 if (dc
.Class("SvtHistoryOptions").GlobalNamespace())
166 if (dc
.Class("SvtSecurityOptions").GlobalNamespace())
168 if (dc
.Class("SvtViewOptions").GlobalNamespace())
170 if (dc
.Class("SvtUserOptions").GlobalNamespace())
172 if (dc
.Class("SvtMenuOptions").GlobalNamespace())
174 if (dc
.Class("SvtPathOptions").GlobalNamespace())
176 if (dc
.Class("SvtSysLocaleOptions").GlobalNamespace())
179 interestingSet
.insert(var
);
183 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr
const* declRefExpr
)
185 if (ignoreLocation(declRefExpr
))
187 auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
190 if (interestingSet
.find(varDecl
) == interestingSet
.end())
192 if (!checkifUnused(declRefExpr
, varDecl
))
193 interestingSet
.erase(varDecl
);
197 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
198 // related VarDecl is unused.
199 bool UnusedVariableMore::checkifUnused(Stmt
const* stmt
, VarDecl
const* varDecl
)
201 const Stmt
* parent
= getParentStmt(stmt
);
204 // check if we're inside a CXXCtorInitializer
205 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
206 if (parentsRange
.begin() != parentsRange
.end())
208 auto parentDecl
= parentsRange
.begin()->get
<Decl
>();
209 if (parentDecl
&& (isa
<CXXConstructorDecl
>(parentDecl
) || isa
<VarDecl
>(parentDecl
)))
212 interestingDebugMap
[varDecl
] = stmt
;
216 if (isa
<ReturnStmt
>(parent
))
218 if (isa
<IfStmt
>(parent
))
220 if (isa
<SwitchStmt
>(parent
))
222 if (isa
<InitListExpr
>(parent
))
224 if (isa
<CXXConstructExpr
>(parent
))
226 if (isa
<BinaryOperator
>(parent
))
228 if (isa
<UnaryOperator
>(parent
))
230 if (isa
<ConditionalOperator
>(parent
))
232 if (isa
<ArraySubscriptExpr
>(parent
))
234 if (isa
<CXXBindTemporaryExpr
>(parent
))
235 return checkifUnused(parent
, varDecl
);
236 if (isa
<MaterializeTemporaryExpr
>(parent
))
237 return checkifUnused(parent
, varDecl
);
239 if (isa
<CompoundStmt
>(parent
))
241 interestingDebugMap
[varDecl
] = parent
;
245 // check for cast to void
246 if (auto explicitCastExpr
= dyn_cast
<ExplicitCastExpr
>(parent
))
248 if (loplugin::TypeCheck(explicitCastExpr
->getTypeAsWritten()).Void())
252 if (isa
<MemberExpr
>(parent
))
253 return checkifUnused(parent
, varDecl
);
254 if (isa
<ExprWithCleanups
>(parent
))
255 return checkifUnused(parent
, varDecl
);
256 if (isa
<CastExpr
>(parent
))
257 return checkifUnused(parent
, varDecl
);
258 if (isa
<ParenExpr
>(parent
))
259 return checkifUnused(parent
, varDecl
);
261 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
263 const CXXMethodDecl
* calleeMethodDecl
264 = dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
265 if (calleeMethodDecl
)
267 if (calleeMethodDecl
->getNumParams() == 0)
268 return checkifUnused(parent
, varDecl
);
269 if (operatorCallExpr
->getArg(0) == stmt
)
270 return checkifUnused(parent
, varDecl
);
273 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
275 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>(stmt
);
276 if (memberExpr
&& memberCallExpr
->getImplicitObjectArgument() == memberExpr
->getBase())
278 // if we are calling a method on the varDecl, walk up
279 if (!checkifUnused(parent
, varDecl
))
281 // check if we are passing something to the var by non-const ref, in which case it is updating something else for us
282 const FunctionDecl
* calleeFunctionDecl
= memberCallExpr
->getDirectCallee();
283 if (calleeFunctionDecl
)
285 if (calleeFunctionDecl
->isVariadic())
287 for (unsigned i
= 0; i
< memberCallExpr
->getNumArgs(); ++i
)
289 if (i
>= calleeFunctionDecl
->getNumParams()) // can happen in template code
291 interestingDebugMap
[varDecl
] = parent
;
294 if (!isOkForParameter(calleeFunctionDecl
->getParamDecl(i
)->getType()))
298 interestingDebugMap
[varDecl
] = parent
;
302 if (isa
<CallExpr
>(parent
))
305 interestingDebugMap
[varDecl
] = parent
;
309 bool UnusedVariableMore::isOkForParameter(const QualType
& qt
)
311 if (qt
->isIntegralOrEnumerationType())
313 auto const type
= loplugin::TypeCheck(qt
);
316 return bool(type
.Pointer().Const());
318 else if (type
.LvalueReference())
320 return bool(type
.LvalueReference().Const());
325 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
327 if (ignoreLocation(functionDecl
))
329 //functionDecl->dump();
333 static Plugin::Registration
<UnusedVariableMore
> X("unusedvariablemore", false);
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */