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 #if CLANG_VERSION >= 110000
21 #include "clang/AST/ParentMapContext.h"
27 This is a compile check. The results of this plugin need to be checked by hand, since it is a collection of heuristics.
29 Check for unused variable where
30 (*) we never call methods that return information from the variable.
31 (*) we never pass the variable to anything else
33 Classes which are safe to be warned about need to be marked using
34 SAL_WARN_UNUSED (see e.g. OUString). For external classes such as std::vector
35 that cannot be edited there is a manual list.
37 This is an expensive plugin, since it walks up the parent tree,
38 so it is off by default.
41 class UnusedVariableMore
: public loplugin::FilteringPlugin
<UnusedVariableMore
>
44 explicit UnusedVariableMore(const InstantiationData
& data
);
45 virtual void run() override
;
46 bool VisitVarDecl(VarDecl
const*);
47 bool VisitDeclRefExpr(DeclRefExpr
const*);
48 bool VisitFunctionDecl(FunctionDecl
const*);
51 bool checkifUnused(Stmt
const*, VarDecl
const*);
52 bool isOkForParameter(const QualType
&);
54 std::unordered_set
<VarDecl
const*> interestingSet
;
55 // used to dump the last place that said the usage was unused, for debug purposes
56 std::unordered_map
<VarDecl
const*, Stmt
const*> interestingDebugMap
;
59 UnusedVariableMore::UnusedVariableMore(const InstantiationData
& data
)
60 : FilteringPlugin(data
)
64 void UnusedVariableMore::run()
66 std::string
fn(handler
.getMainFileName());
67 loplugin::normalizeDotDotInFilePath(fn
);
70 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sal/qa/"))
72 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/i18npool/qa/"))
74 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/qa/"))
77 // vector of shared_ptr used to delay destruction
78 if (fn
== SRCDIR
"/cppuhelper/source/servicemanager.cxx")
80 if (fn
== SRCDIR
"/i18nlangtag/source/languagetag/languagetag.cxx")
82 // unordered_set of Reference to delay destruction
83 if (fn
== SRCDIR
"/stoc/source/servicemanager/servicemanager.cxx")
85 // TODO "operator >>" fooling me here
86 if (fn
== SRCDIR
"/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
89 if (fn
== SRCDIR
"/sfx2/source/dialog/dinfdlg.cxx")
91 // std::vector< Reference< XInterface > > keep alive
92 if (fn
== SRCDIR
"/dbaccess/source/core/dataaccess/databasedocument.cxx")
95 if (fn
== SRCDIR
"/sc/source/core/tool/scmatrix.cxx")
97 // storing local copy of Link<>
98 if (fn
== SRCDIR
"/sc/source/ui/miscdlgs/simpref.cxx")
100 // Using an SwPaM to do stuff
101 if (fn
== SRCDIR
"/sw/source/core/crsr/bookmark.cxx")
103 // index variable in for loop?
104 if (fn
== SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx")
106 // TODO "operator >>" fooling me here
107 if (fn
== SRCDIR
"/sw/source/filter/ww8/ww8par.cxx")
109 // TODO "operator >>" fooling me here
110 if (fn
== SRCDIR
"/sc/source/filter/excel/xistream.cxx")
113 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
115 for (VarDecl
const* varDecl
: interestingSet
)
117 report(DiagnosticsEngine::Warning
, "unused variable %0", varDecl
->getLocation())
118 << varDecl
->getDeclName();
119 //auto it = interestingDebugMap.find(varDecl);
120 //if (it != interestingDebugMap.end())
121 // it->second->dump();
125 bool isWarnUnusedType(QualType type
)
127 if (auto const t
= type
->getAs
<TypedefType
>())
129 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
134 if (auto const t
= type
->getAs
<RecordType
>())
136 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
141 return loplugin::isExtraWarnUnusedType(type
);
144 bool UnusedVariableMore::VisitVarDecl(VarDecl
const* var
)
146 if (ignoreLocation(var
))
148 if (var
->isDefinedOutsideFunctionOrMethod())
150 if (isa
<ParmVarDecl
>(var
))
152 if (!isWarnUnusedType(var
->getType()))
156 auto dc
= loplugin::TypeCheck(var
->getType());
157 if (dc
.Class("ZCodec").GlobalNamespace())
159 if (dc
.Class("ScopedVclPtrInstance").GlobalNamespace())
161 if (dc
.Class("VclPtrInstance").GlobalNamespace())
163 if (dc
.Class("Config").GlobalNamespace())
165 // I think these classes modify global state somehow
166 if (dc
.Class("SvtHistoryOptions").GlobalNamespace())
168 if (dc
.Class("SvtSecurityOptions").GlobalNamespace())
170 if (dc
.Class("SvtViewOptions").GlobalNamespace())
172 if (dc
.Class("SvtUserOptions").GlobalNamespace())
174 if (dc
.Class("SvtMenuOptions").GlobalNamespace())
176 if (dc
.Class("SvtPathOptions").GlobalNamespace())
178 if (dc
.Class("SvtSysLocaleOptions").GlobalNamespace())
181 interestingSet
.insert(var
);
185 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr
const* declRefExpr
)
187 if (ignoreLocation(declRefExpr
))
189 auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
192 if (interestingSet
.find(varDecl
) == interestingSet
.end())
194 if (!checkifUnused(declRefExpr
, varDecl
))
195 interestingSet
.erase(varDecl
);
199 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
200 // related VarDecl is unused.
201 bool UnusedVariableMore::checkifUnused(Stmt
const* stmt
, VarDecl
const* varDecl
)
203 const Stmt
* parent
= getParentStmt(stmt
);
206 // check if we're inside a CXXCtorInitializer
207 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
208 if (parentsRange
.begin() != parentsRange
.end())
210 auto parentDecl
= parentsRange
.begin()->get
<Decl
>();
211 if (parentDecl
&& (isa
<CXXConstructorDecl
>(parentDecl
) || isa
<VarDecl
>(parentDecl
)))
214 interestingDebugMap
[varDecl
] = stmt
;
218 if (isa
<ReturnStmt
>(parent
))
220 if (isa
<IfStmt
>(parent
))
222 if (isa
<SwitchStmt
>(parent
))
224 if (isa
<InitListExpr
>(parent
))
226 if (isa
<CXXConstructExpr
>(parent
))
228 if (isa
<BinaryOperator
>(parent
))
230 if (isa
<UnaryOperator
>(parent
))
232 if (isa
<ConditionalOperator
>(parent
))
234 if (isa
<ArraySubscriptExpr
>(parent
))
236 if (isa
<CXXBindTemporaryExpr
>(parent
))
237 return checkifUnused(parent
, varDecl
);
238 if (isa
<MaterializeTemporaryExpr
>(parent
))
239 return checkifUnused(parent
, varDecl
);
241 if (isa
<CompoundStmt
>(parent
))
243 interestingDebugMap
[varDecl
] = parent
;
247 // check for cast to void
248 if (auto explicitCastExpr
= dyn_cast
<ExplicitCastExpr
>(parent
))
250 if (loplugin::TypeCheck(explicitCastExpr
->getTypeAsWritten()).Void())
254 if (isa
<MemberExpr
>(parent
))
255 return checkifUnused(parent
, varDecl
);
256 if (isa
<ExprWithCleanups
>(parent
))
257 return checkifUnused(parent
, varDecl
);
258 if (isa
<CastExpr
>(parent
))
259 return checkifUnused(parent
, varDecl
);
260 if (isa
<ParenExpr
>(parent
))
261 return checkifUnused(parent
, varDecl
);
263 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
265 const CXXMethodDecl
* calleeMethodDecl
266 = dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
267 if (calleeMethodDecl
)
269 if (calleeMethodDecl
->getNumParams() == 0)
270 return checkifUnused(parent
, varDecl
);
271 if (operatorCallExpr
->getArg(0) == stmt
)
272 return checkifUnused(parent
, varDecl
);
275 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
277 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>(stmt
);
278 if (memberExpr
&& memberCallExpr
->getImplicitObjectArgument() == memberExpr
->getBase())
280 // if we are calling a method on the varDecl, walk up
281 if (!checkifUnused(parent
, varDecl
))
283 // check if we are passing something to the var by non-const ref, in which case it is updating something else for us
284 const FunctionDecl
* calleeFunctionDecl
= memberCallExpr
->getDirectCallee();
285 if (calleeFunctionDecl
)
287 if (calleeFunctionDecl
->isVariadic())
289 for (unsigned i
= 0; i
< memberCallExpr
->getNumArgs(); ++i
)
291 if (i
>= calleeFunctionDecl
->getNumParams()) // can happen in template code
293 interestingDebugMap
[varDecl
] = parent
;
296 if (!isOkForParameter(calleeFunctionDecl
->getParamDecl(i
)->getType()))
300 interestingDebugMap
[varDecl
] = parent
;
304 if (isa
<CallExpr
>(parent
))
307 interestingDebugMap
[varDecl
] = parent
;
311 bool UnusedVariableMore::isOkForParameter(const QualType
& qt
)
313 if (qt
->isIntegralOrEnumerationType())
315 auto const type
= loplugin::TypeCheck(qt
);
318 return bool(type
.Pointer().Const());
320 else if (type
.LvalueReference())
322 return bool(type
.LvalueReference().Const());
327 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
329 if (ignoreLocation(functionDecl
))
331 //functionDecl->dump();
335 static Plugin::Registration
<UnusedVariableMore
> X("unusedvariablemore", false);
339 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */