LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / unusedvariablemore.cxx
blobf165a6d6b50c8ec5b72820e89221f259da4ec02b
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_clang.h"
13 #include <config_global.h>
14 #include "plugin.hxx"
15 #include "compat.hxx"
16 #include "check.hxx"
17 #include <unordered_set>
18 #include <unordered_map>
20 #if CLANG_VERSION >= 110000
21 #include "clang/AST/ParentMapContext.h"
22 #endif
24 namespace loplugin
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>
43 public:
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*);
50 private:
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);
69 // ignore QA folders
70 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/"))
71 return;
72 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/i18npool/qa/"))
73 return;
74 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/qa/"))
75 return;
77 // vector of shared_ptr used to delay destruction
78 if (fn == SRCDIR "/cppuhelper/source/servicemanager.cxx")
79 return;
80 if (fn == SRCDIR "/i18nlangtag/source/languagetag/languagetag.cxx")
81 return;
82 // unordered_set of Reference to delay destruction
83 if (fn == SRCDIR "/stoc/source/servicemanager/servicemanager.cxx")
84 return;
85 // TODO "operator >>" fooling me here
86 if (fn == SRCDIR "/editeng/source/accessibility/AccessibleEditableTextPara.cxx")
87 return;
88 // some weird stuff
89 if (fn == SRCDIR "/sfx2/source/dialog/dinfdlg.cxx")
90 return;
91 // std::vector< Reference< XInterface > > keep alive
92 if (fn == SRCDIR "/dbaccess/source/core/dataaccess/databasedocument.cxx")
93 return;
94 // template magic
95 if (fn == SRCDIR "/sc/source/core/tool/scmatrix.cxx")
96 return;
97 // storing local copy of Link<>
98 if (fn == SRCDIR "/sc/source/ui/miscdlgs/simpref.cxx")
99 return;
100 // Using an SwPaM to do stuff
101 if (fn == SRCDIR "/sw/source/core/crsr/bookmark.cxx")
102 return;
103 // index variable in for loop?
104 if (fn == SRCDIR "/sw/source/uibase/docvw/edtwin.cxx")
105 return;
106 // TODO "operator >>" fooling me here
107 if (fn == SRCDIR "/sw/source/filter/ww8/ww8par.cxx")
108 return;
109 // TODO "operator >>" fooling me here
110 if (fn == SRCDIR "/sc/source/filter/excel/xistream.cxx")
111 return;
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>())
131 return true;
134 if (auto const t = type->getAs<RecordType>())
136 if (t->getDecl()->hasAttr<WarnUnusedAttr>())
138 return true;
141 return loplugin::isExtraWarnUnusedType(type);
144 bool UnusedVariableMore::VisitVarDecl(VarDecl const* var)
146 if (ignoreLocation(var))
147 return true;
148 if (var->isDefinedOutsideFunctionOrMethod())
149 return true;
150 if (isa<ParmVarDecl>(var))
151 return true;
152 if (!isWarnUnusedType(var->getType()))
153 return true;
155 // some false +
156 auto dc = loplugin::TypeCheck(var->getType());
157 if (dc.Class("ZCodec").GlobalNamespace())
158 return true;
159 if (dc.Class("ScopedVclPtrInstance").GlobalNamespace())
160 return true;
161 if (dc.Class("VclPtrInstance").GlobalNamespace())
162 return true;
163 if (dc.Class("Config").GlobalNamespace())
164 return true;
165 // I think these classes modify global state somehow
166 if (dc.Class("SvtHistoryOptions").GlobalNamespace())
167 return true;
168 if (dc.Class("SvtSecurityOptions").GlobalNamespace())
169 return true;
170 if (dc.Class("SvtViewOptions").GlobalNamespace())
171 return true;
172 if (dc.Class("SvtUserOptions").GlobalNamespace())
173 return true;
174 if (dc.Class("SvtMenuOptions").GlobalNamespace())
175 return true;
176 if (dc.Class("SvtPathOptions").GlobalNamespace())
177 return true;
178 if (dc.Class("SvtSysLocaleOptions").GlobalNamespace())
179 return true;
181 interestingSet.insert(var);
182 return true;
185 bool UnusedVariableMore::VisitDeclRefExpr(DeclRefExpr const* declRefExpr)
187 if (ignoreLocation(declRefExpr))
188 return true;
189 auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
190 if (!varDecl)
191 return true;
192 if (interestingSet.find(varDecl) == interestingSet.end())
193 return true;
194 if (!checkifUnused(declRefExpr, varDecl))
195 interestingSet.erase(varDecl);
196 return true;
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);
204 if (!parent)
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)))
212 return false;
214 interestingDebugMap[varDecl] = stmt;
215 return true;
218 if (isa<ReturnStmt>(parent))
219 return false;
220 if (isa<IfStmt>(parent))
221 return false;
222 if (isa<SwitchStmt>(parent))
223 return false;
224 if (isa<InitListExpr>(parent))
225 return false;
226 if (isa<CXXConstructExpr>(parent))
227 return false;
228 if (isa<BinaryOperator>(parent))
229 return false;
230 if (isa<UnaryOperator>(parent))
231 return false;
232 if (isa<ConditionalOperator>(parent))
233 return false;
234 if (isa<ArraySubscriptExpr>(parent))
235 return false;
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;
244 return true;
247 // check for cast to void
248 if (auto explicitCastExpr = dyn_cast<ExplicitCastExpr>(parent))
250 if (loplugin::TypeCheck(explicitCastExpr->getTypeAsWritten()).Void())
251 return false;
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))
282 return false;
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())
288 return false;
289 for (unsigned i = 0; i < memberCallExpr->getNumArgs(); ++i)
291 if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
293 interestingDebugMap[varDecl] = parent;
294 return true;
296 if (!isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType()))
297 return false;
300 interestingDebugMap[varDecl] = parent;
301 return true;
304 if (isa<CallExpr>(parent))
305 return false;
307 interestingDebugMap[varDecl] = parent;
308 return true;
311 bool UnusedVariableMore::isOkForParameter(const QualType& qt)
313 if (qt->isIntegralOrEnumerationType())
314 return true;
315 auto const type = loplugin::TypeCheck(qt);
316 if (type.Pointer())
318 return bool(type.Pointer().Const());
320 else if (type.LvalueReference())
322 return bool(type.LvalueReference().Const());
324 return false;
327 bool UnusedVariableMore::VisitFunctionDecl(FunctionDecl const* functionDecl)
329 if (ignoreLocation(functionDecl))
330 return true;
331 //functionDecl->dump();
332 return true;
335 static Plugin::Registration<UnusedVariableMore> X("unusedvariablemore", false);
337 } // namespace
339 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */