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 RecursiveASTVisitor
<UnusedVariableMore
>, public Plugin
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
)
59 static bool startswith(const std::string
& rStr
, const char* pSubStr
)
61 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
64 void UnusedVariableMore::run()
66 std::string
fn(compiler
.getSourceManager()
67 .getFileEntryForID(compiler
.getSourceManager().getMainFileID())
69 loplugin::normalizeDotDotInFilePath(fn
);
72 if (startswith(fn
, SRCDIR
"/sal/qa/"))
74 if (startswith(fn
, SRCDIR
"/i18npool/qa/"))
76 if (startswith(fn
, SRCDIR
"/sc/qa/"))
79 // vector of shared_ptr used to delay destruction
80 if (fn
== SRCDIR
"/cppuhelper/source/servicemanager.cxx")
82 if (fn
== SRCDIR
"/i18nlangtag/source/languagetag/languagetag.cxx")
84 if (fn
== SRCDIR
"/vcl/workben/outdevgrind.cxx")
86 // unordered_set of Reference to delay destruction
87 if (fn
== SRCDIR
"/stoc/source/servicemanager/servicemanager.cxx")
89 // TODO "operator >>" fooling me here
90 if (fn
== SRCDIR
"/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
93 if (fn
== SRCDIR
"/sfx2/source/dialog/dinfdlg.cxx")
95 // std::vector< Reference< XInterface > > keep alive
96 if (fn
== SRCDIR
"/dbaccess/source/core/dataaccess/databasedocument.cxx")
99 if (fn
== SRCDIR
"/sc/source/core/tool/scmatrix.cxx")
101 // storing local copy of Link<>
102 if (fn
== SRCDIR
"/sc/source/ui/miscdlgs/simpref.cxx")
104 // Using an SwPaM to do stuff
105 if (fn
== SRCDIR
"/sw/source/core/crsr/bookmrk.cxx")
107 // index variable in for loop?
108 if (fn
== SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx")
110 // TODO "operator >>" fooling me here
111 if (fn
== SRCDIR
"/sw/source/filter/ww8/ww8par.cxx")
113 // TODO "operator >>" fooling me here
114 if (fn
== SRCDIR
"/sc/source/filter/excel/xistream.cxx")
117 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
119 for (VarDecl
const* varDecl
: interestingSet
)
121 report(DiagnosticsEngine::Warning
, "unused variable %0", varDecl
->getLocation())
122 << varDecl
->getDeclName();
123 //auto it = interestingDebugMap.find(varDecl);
124 //if (it != interestingDebugMap.end())
125 // it->second->dump();
129 bool isWarnUnusedType(QualType type
)
131 if (auto const t
= type
->getAs
<TypedefType
>())
133 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
138 if (auto const t
= type
->getAs
<RecordType
>())
140 if (t
->getDecl()->hasAttr
<WarnUnusedAttr
>())
145 return loplugin::isExtraWarnUnusedType(type
);
148 bool UnusedVariableMore::VisitVarDecl(VarDecl
const* var
)
150 if (ignoreLocation(var
))
152 if (var
->isDefinedOutsideFunctionOrMethod())
154 if (isa
<ParmVarDecl
>(var
))
156 if (!isWarnUnusedType(var
->getType()))
160 auto dc
= loplugin::TypeCheck(var
->getType());
161 if (dc
.Class("ZCodec").GlobalNamespace())
163 if (dc
.Class("ScopedVclPtrInstance").GlobalNamespace())
165 if (dc
.Class("VclPtrInstance").GlobalNamespace())
167 if (dc
.Class("Config").GlobalNamespace())
169 // I think these classes modify global state somehow
170 if (dc
.Class("SvtHistoryOptions").GlobalNamespace())
172 if (dc
.Class("SvtSecurityOptions").GlobalNamespace())
174 if (dc
.Class("SvtViewOptions").GlobalNamespace())
176 if (dc
.Class("SvtUserOptions").GlobalNamespace())
178 if (dc
.Class("SvtFontOptions").GlobalNamespace())
180 if (dc
.Class("SvtMenuOptions").GlobalNamespace())
182 if (dc
.Class("SvtPathOptions").GlobalNamespace())
184 if (dc
.Class("SvtPrintWarningOptions").GlobalNamespace())
186 if (dc
.Class("SvtSysLocaleOptions").GlobalNamespace())
189 interestingSet
.insert(var
);
193 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr
const* declRefExpr
)
195 if (ignoreLocation(declRefExpr
))
197 auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
200 if (interestingSet
.find(varDecl
) == interestingSet
.end())
202 if (!checkifUnused(declRefExpr
, varDecl
))
203 interestingSet
.erase(varDecl
);
207 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
208 // related VarDecl is unused.
209 bool UnusedVariableMore::checkifUnused(Stmt
const* stmt
, VarDecl
const* varDecl
)
211 const Stmt
* parent
= getParentStmt(stmt
);
214 // check if we're inside a CXXCtorInitializer
215 auto parentsRange
= compiler
.getASTContext().getParents(*stmt
);
216 if (parentsRange
.begin() != parentsRange
.end())
218 auto parentDecl
= parentsRange
.begin()->get
<Decl
>();
219 if (parentDecl
&& (isa
<CXXConstructorDecl
>(parentDecl
) || isa
<VarDecl
>(parentDecl
)))
222 interestingDebugMap
[varDecl
] = stmt
;
226 if (isa
<ReturnStmt
>(parent
))
228 if (isa
<IfStmt
>(parent
))
230 if (isa
<SwitchStmt
>(parent
))
232 if (isa
<InitListExpr
>(parent
))
234 if (isa
<CXXConstructExpr
>(parent
))
236 if (isa
<BinaryOperator
>(parent
))
238 if (isa
<UnaryOperator
>(parent
))
240 if (isa
<ConditionalOperator
>(parent
))
242 if (isa
<ArraySubscriptExpr
>(parent
))
244 if (isa
<CXXBindTemporaryExpr
>(parent
))
245 return checkifUnused(parent
, varDecl
);
246 if (isa
<MaterializeTemporaryExpr
>(parent
))
247 return checkifUnused(parent
, varDecl
);
249 if (isa
<CompoundStmt
>(parent
))
251 interestingDebugMap
[varDecl
] = parent
;
255 // check for cast to void
256 if (auto explicitCastExpr
= dyn_cast
<ExplicitCastExpr
>(parent
))
258 if (loplugin::TypeCheck(explicitCastExpr
->getTypeAsWritten()).Void())
262 if (isa
<MemberExpr
>(parent
))
263 return checkifUnused(parent
, varDecl
);
264 if (isa
<ExprWithCleanups
>(parent
))
265 return checkifUnused(parent
, varDecl
);
266 if (isa
<CastExpr
>(parent
))
267 return checkifUnused(parent
, varDecl
);
268 if (isa
<ParenExpr
>(parent
))
269 return checkifUnused(parent
, varDecl
);
271 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
273 const CXXMethodDecl
* calleeMethodDecl
274 = dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
275 if (calleeMethodDecl
)
277 if (calleeMethodDecl
->getNumParams() == 0)
278 return checkifUnused(parent
, varDecl
);
279 if (operatorCallExpr
->getArg(0) == stmt
)
280 return checkifUnused(parent
, varDecl
);
283 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
285 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>(stmt
);
286 if (memberExpr
&& memberCallExpr
->getImplicitObjectArgument() == memberExpr
->getBase())
288 // if we are calling a method on the varDecl, walk up
289 if (!checkifUnused(parent
, varDecl
))
291 // check if we are passing something to the var by non-const ref, in which case it is updating something else for us
292 const FunctionDecl
* calleeFunctionDecl
= memberCallExpr
->getDirectCallee();
293 if (calleeFunctionDecl
)
295 if (calleeFunctionDecl
->isVariadic())
297 for (unsigned i
= 0; i
< memberCallExpr
->getNumArgs(); ++i
)
299 if (i
>= calleeFunctionDecl
->getNumParams()) // can happen in template code
301 interestingDebugMap
[varDecl
] = parent
;
304 if (!isOkForParameter(calleeFunctionDecl
->getParamDecl(i
)->getType()))
308 interestingDebugMap
[varDecl
] = parent
;
312 if (isa
<CallExpr
>(parent
))
315 interestingDebugMap
[varDecl
] = parent
;
319 bool UnusedVariableMore::isOkForParameter(const QualType
& qt
)
321 if (qt
->isIntegralOrEnumerationType())
323 auto const type
= loplugin::TypeCheck(qt
);
326 return bool(type
.Pointer().Const());
328 else if (type
.LvalueReference())
330 return bool(type
.LvalueReference().Const());
335 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
337 if (ignoreLocation(functionDecl
))
339 //functionDecl->dump();
343 static Plugin::Registration
<UnusedVariableMore
> X("unusedvariablemore", false);
347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */