bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / unusedvariablemore.cxx
blob8c153164f1747ba6151bdb6d9b6898bf3f3caedf
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 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include <config_global.h>
13 #include "plugin.hxx"
14 #include "compat.hxx"
15 #include "check.hxx"
16 #include <unordered_set>
17 #include <unordered_map>
19 namespace loplugin
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>
38 public:
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*);
45 private:
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);
64 // ignore QA folders
65 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/"))
66 return;
67 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/i18npool/qa/"))
68 return;
69 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/qa/"))
70 return;
72 // vector of shared_ptr used to delay destruction
73 if (fn == SRCDIR "/cppuhelper/source/servicemanager.cxx")
74 return;
75 if (fn == SRCDIR "/i18nlangtag/source/languagetag/languagetag.cxx")
76 return;
77 if (fn == SRCDIR "/vcl/workben/outdevgrind.cxx")
78 return;
79 // unordered_set of Reference to delay destruction
80 if (fn == SRCDIR "/stoc/source/servicemanager/servicemanager.cxx")
81 return;
82 // TODO "operator >>" fooling me here
83 if (fn == SRCDIR "/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
84 return;
85 // some weird stuff
86 if (fn == SRCDIR "/sfx2/source/dialog/dinfdlg.cxx")
87 return;
88 // std::vector< Reference< XInterface > > keep alive
89 if (fn == SRCDIR "/dbaccess/source/core/dataaccess/databasedocument.cxx")
90 return;
91 // template magic
92 if (fn == SRCDIR "/sc/source/core/tool/scmatrix.cxx")
93 return;
94 // storing local copy of Link<>
95 if (fn == SRCDIR "/sc/source/ui/miscdlgs/simpref.cxx")
96 return;
97 // Using an SwPaM to do stuff
98 if (fn == SRCDIR "/sw/source/core/crsr/bookmrk.cxx")
99 return;
100 // index variable in for loop?
101 if (fn == SRCDIR "/sw/source/uibase/docvw/edtwin.cxx")
102 return;
103 // TODO "operator >>" fooling me here
104 if (fn == SRCDIR "/sw/source/filter/ww8/ww8par.cxx")
105 return;
106 // TODO "operator >>" fooling me here
107 if (fn == SRCDIR "/sc/source/filter/excel/xistream.cxx")
108 return;
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>())
128 return true;
131 if (auto const t = type->getAs<RecordType>())
133 if (t->getDecl()->hasAttr<WarnUnusedAttr>())
135 return true;
138 return loplugin::isExtraWarnUnusedType(type);
141 bool UnusedVariableMore::VisitVarDecl(VarDecl const* var)
143 if (ignoreLocation(var))
144 return true;
145 if (var->isDefinedOutsideFunctionOrMethod())
146 return true;
147 if (isa<ParmVarDecl>(var))
148 return true;
149 if (!isWarnUnusedType(var->getType()))
150 return true;
152 // some false +
153 auto dc = loplugin::TypeCheck(var->getType());
154 if (dc.Class("ZCodec").GlobalNamespace())
155 return true;
156 if (dc.Class("ScopedVclPtrInstance").GlobalNamespace())
157 return true;
158 if (dc.Class("VclPtrInstance").GlobalNamespace())
159 return true;
160 if (dc.Class("Config").GlobalNamespace())
161 return true;
162 // I think these classes modify global state somehow
163 if (dc.Class("SvtHistoryOptions").GlobalNamespace())
164 return true;
165 if (dc.Class("SvtSecurityOptions").GlobalNamespace())
166 return true;
167 if (dc.Class("SvtViewOptions").GlobalNamespace())
168 return true;
169 if (dc.Class("SvtUserOptions").GlobalNamespace())
170 return true;
171 if (dc.Class("SvtFontOptions").GlobalNamespace())
172 return true;
173 if (dc.Class("SvtMenuOptions").GlobalNamespace())
174 return true;
175 if (dc.Class("SvtPathOptions").GlobalNamespace())
176 return true;
177 if (dc.Class("SvtPrintWarningOptions").GlobalNamespace())
178 return true;
179 if (dc.Class("SvtSysLocaleOptions").GlobalNamespace())
180 return true;
182 interestingSet.insert(var);
183 return true;
186 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr const* declRefExpr)
188 if (ignoreLocation(declRefExpr))
189 return true;
190 auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
191 if (!varDecl)
192 return true;
193 if (interestingSet.find(varDecl) == interestingSet.end())
194 return true;
195 if (!checkifUnused(declRefExpr, varDecl))
196 interestingSet.erase(varDecl);
197 return true;
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);
205 if (!parent)
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)))
213 return false;
215 interestingDebugMap[varDecl] = stmt;
216 return true;
219 if (isa<ReturnStmt>(parent))
220 return false;
221 if (isa<IfStmt>(parent))
222 return false;
223 if (isa<SwitchStmt>(parent))
224 return false;
225 if (isa<InitListExpr>(parent))
226 return false;
227 if (isa<CXXConstructExpr>(parent))
228 return false;
229 if (isa<BinaryOperator>(parent))
230 return false;
231 if (isa<UnaryOperator>(parent))
232 return false;
233 if (isa<ConditionalOperator>(parent))
234 return false;
235 if (isa<ArraySubscriptExpr>(parent))
236 return false;
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;
245 return true;
248 // check for cast to void
249 if (auto explicitCastExpr = dyn_cast<ExplicitCastExpr>(parent))
251 if (loplugin::TypeCheck(explicitCastExpr->getTypeAsWritten()).Void())
252 return false;
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))
283 return false;
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())
289 return false;
290 for (unsigned i = 0; i < memberCallExpr->getNumArgs(); ++i)
292 if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
294 interestingDebugMap[varDecl] = parent;
295 return true;
297 if (!isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType()))
298 return false;
301 interestingDebugMap[varDecl] = parent;
302 return true;
305 if (isa<CallExpr>(parent))
306 return false;
308 interestingDebugMap[varDecl] = parent;
309 return true;
312 bool UnusedVariableMore::isOkForParameter(const QualType& qt)
314 if (qt->isIntegralOrEnumerationType())
315 return true;
316 auto const type = loplugin::TypeCheck(qt);
317 if (type.Pointer())
319 return bool(type.Pointer().Const());
321 else if (type.LvalueReference())
323 return bool(type.LvalueReference().Const());
325 return false;
328 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl const* functionDecl)
330 if (ignoreLocation(functionDecl))
331 return true;
332 //functionDecl->dump();
333 return true;
336 static Plugin::Registration<UnusedVariableMore> X("unusedvariablemore", false);
338 } // namespace
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */