nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / xmlimport.cxx
blobb18554dfc768a895f3547c50cc360c929101997f
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.
11 #ifndef LO_CLANG_SHARED_PLUGINS
13 #include "compat.hxx"
14 #include "plugin.hxx"
15 #include "check.hxx"
16 #include <iostream>
17 #include <unordered_map>
18 #include "clang/AST/CXXInheritance.h"
21 * This is a compile-time checker.
23 * Check that when we override SvXmlImportContext, and we override createFastChildContext,
24 * we have also overridden startFastElement, or the fast-parser stuff will not work
25 * correctly.
28 namespace
30 class XmlImport : public loplugin::FilteringPlugin<XmlImport>
32 public:
33 explicit XmlImport(loplugin::InstantiationData const& data)
34 : FilteringPlugin(data)
38 bool preRun() override
40 StringRef fn(handler.getMainFileName());
41 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/core/xmlictxt.cxx"))
42 return false;
43 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/core/xmlimp.cxx"))
44 return false;
45 // These are mostly classes delegating calls to other classes
46 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/text/XMLTextFrameContext.cxx"))
47 return false;
48 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/draw/ximpshap.cxx"))
49 return false;
50 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/table/XMLTableImport.cxx"))
51 return false;
52 if (loplugin::isSamePathname(fn,
53 SRCDIR "/sc/source/filter/xml/XMLTrackedChangesContext.cxx"))
54 return false;
55 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/xml/xmlannoi.cxx"))
56 return false;
57 // this class specifically wants to prevent some endFastElement processing happening in its superclass
58 if (loplugin::isSamePathname(fn, SRCDIR
59 "/xmloff/source/text/XMLIndexBibliographySourceContext.cxx"))
60 return false;
61 return true;
64 void run() override
66 if (preRun())
68 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
72 bool VisitCXXMethodDecl(const CXXMethodDecl*);
73 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr*);
74 bool VisitBinaryOperator(const BinaryOperator*);
75 bool VisitSwitchStmt(const SwitchStmt*);
76 bool VisitCallExpr(const CallExpr*);
78 private:
79 bool isXmlTokEnum(const Expr*);
80 bool isUInt16(const Expr*);
81 bool isUInt16(QualType);
83 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> startFastElementSet;
84 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> StartElementSet;
85 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> endFastElementSet;
86 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> EndElementSet;
87 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> charactersSet;
88 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> CharactersSet;
91 bool XmlImport::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
93 auto beginLoc = compat::getBeginLoc(methodDecl);
94 if (!beginLoc.isValid() || ignoreLocation(beginLoc))
95 return true;
97 if (!methodDecl->getIdentifier())
98 return true;
100 auto cxxRecordDecl = methodDecl->getParent();
101 if (!cxxRecordDecl || !cxxRecordDecl->getIdentifier())
102 return true;
104 if (loplugin::DeclCheck(cxxRecordDecl).Class("SvXMLImportContext"))
105 return true;
107 if (!loplugin::isDerivedFrom(cxxRecordDecl, [](Decl const* decl) -> bool {
108 auto const dc = loplugin::DeclCheck(decl);
109 return bool(dc.ClassOrStruct("SvXMLImportContext").GlobalNamespace());
111 return true;
113 auto name = methodDecl->getName();
114 if (name == "startFastElement")
115 startFastElementSet.insert({ cxxRecordDecl, methodDecl });
116 else if (name == "StartElement")
117 StartElementSet.insert({ cxxRecordDecl, methodDecl });
118 else if (name == "endFastElement")
119 endFastElementSet.insert({ cxxRecordDecl, methodDecl });
120 else if (name == "EndElement")
121 EndElementSet.insert({ cxxRecordDecl, methodDecl });
122 else if (name == "characters")
124 if (methodDecl->getNumParams() == 1)
125 charactersSet.insert({ cxxRecordDecl, methodDecl });
127 else if (name == "Characters")
129 if (methodDecl->getNumParams() == 1)
130 CharactersSet.insert({ cxxRecordDecl, methodDecl });
134 auto it1 = endFastElementSet.find(cxxRecordDecl);
135 auto it2 = EndElementSet.find(cxxRecordDecl);
136 if (it1 != endFastElementSet.end() && it2 != EndElementSet.end())
138 auto methodDecl1 = it1->second;
139 report(DiagnosticsEngine::Warning, "cannot override both endFastElement and EndElement",
140 compat::getBeginLoc(methodDecl1))
141 << methodDecl1->getSourceRange();
142 auto methodDecl2 = it2->second;
143 report(DiagnosticsEngine::Warning, "cannot override both endFastElement and EndElement",
144 compat::getBeginLoc(methodDecl2))
145 << methodDecl2->getSourceRange();
150 auto it1 = startFastElementSet.find(cxxRecordDecl);
151 auto it2 = StartElementSet.find(cxxRecordDecl);
152 if (it1 != startFastElementSet.end() && it2 != StartElementSet.end())
154 auto methodDecl1 = it1->second;
155 report(DiagnosticsEngine::Warning,
156 "cannot override both startFastElement and StartElement",
157 compat::getBeginLoc(methodDecl1))
158 << methodDecl1->getSourceRange();
159 auto methodDecl2 = it2->second;
160 report(DiagnosticsEngine::Warning,
161 "cannot override both startFastElement and StartElement",
162 compat::getBeginLoc(methodDecl2))
163 << methodDecl2->getSourceRange();
167 auto it1 = charactersSet.find(cxxRecordDecl);
168 auto it2 = CharactersSet.find(cxxRecordDecl);
169 if (it1 != charactersSet.end() && it2 != CharactersSet.end())
171 auto methodDecl1 = it1->second;
172 report(DiagnosticsEngine::Warning, "cannot override both characters and Characters",
173 compat::getBeginLoc(methodDecl1))
174 << methodDecl1->getSourceRange();
175 auto methodDecl2 = it2->second;
176 report(DiagnosticsEngine::Warning, "cannot override both characters and Characters",
177 compat::getBeginLoc(methodDecl2))
178 << methodDecl2->getSourceRange();
182 auto checkEmpty = [&]() {
183 if (!methodDecl->isThisDeclarationADefinition())
184 return;
185 auto compoundStmt = dyn_cast_or_null<CompoundStmt>(methodDecl->getBody());
186 if (compoundStmt == nullptr || compoundStmt->size() > 0)
187 return;
188 report(DiagnosticsEngine::Warning, "empty, should be removed",
189 compat::getBeginLoc(methodDecl))
190 << methodDecl->getSourceRange();
191 auto canonicalDecl = methodDecl->getCanonicalDecl();
192 if (canonicalDecl != methodDecl)
193 report(DiagnosticsEngine::Note, "definition here", compat::getBeginLoc(canonicalDecl))
194 << canonicalDecl->getSourceRange();
196 auto checkOnlyReturn = [&]() {
197 if (!methodDecl->isThisDeclarationADefinition())
198 return;
199 auto compoundStmt = dyn_cast_or_null<CompoundStmt>(methodDecl->getBody());
200 if (compoundStmt == nullptr || compoundStmt->size() > 1)
201 return;
202 auto returnStmt = dyn_cast_or_null<ReturnStmt>(*compoundStmt->body_begin());
203 if (!returnStmt)
204 return;
205 auto cxxConstructExpr
206 = dyn_cast_or_null<CXXConstructExpr>(returnStmt->getRetValue()->IgnoreImplicit());
207 if (!cxxConstructExpr)
208 return;
209 if (cxxConstructExpr->getNumArgs() != 1)
210 return;
211 if (!isa<CXXNullPtrLiteralExpr>(cxxConstructExpr->getArg(0)->IgnoreImplicit()))
212 return;
213 report(DiagnosticsEngine::Warning, "empty, should be removed",
214 compat::getBeginLoc(methodDecl))
215 << methodDecl->getSourceRange();
216 auto canonicalDecl = methodDecl->getCanonicalDecl();
217 if (canonicalDecl != methodDecl)
218 report(DiagnosticsEngine::Note, "definition here", compat::getBeginLoc(canonicalDecl))
219 << canonicalDecl->getSourceRange();
222 if (name == "startFastElement")
223 checkEmpty();
224 else if (name == "endFastElement")
225 checkEmpty();
226 else if (name == "characters")
227 checkEmpty();
228 else if (name == "createFastChildContext")
229 checkOnlyReturn();
230 else if (name == "createUnknownChildContext")
231 checkOnlyReturn();
233 return true;
236 bool XmlImport::VisitCXXMemberCallExpr(const CXXMemberCallExpr* callExpr)
238 auto beginLoc = compat::getBeginLoc(callExpr);
239 if (!beginLoc.isValid() || ignoreLocation(callExpr))
240 return true;
242 CXXMethodDecl* methodDecl = callExpr->getMethodDecl();
243 if (!methodDecl || !methodDecl->getIdentifier())
244 return true;
246 auto cxxRecordDecl = methodDecl->getParent();
247 if (!cxxRecordDecl || !cxxRecordDecl->getIdentifier())
248 return true;
250 if (!loplugin::DeclCheck(cxxRecordDecl).Class("SvXMLImportContext"))
251 return true;
253 auto name = methodDecl->getName();
254 if (name == "startFastElement" || name == "characters" || name == "endFastElement"
255 || name == "createFastChildContext" || name == "createUnknownChildContext"
256 || name == "StartElement" || name == "EndElement" || name == "Characters"
257 || name == "CreateChildContext")
260 * Calling this superclass method from a subclass method will mess with the fallback logic in the superclass.
262 report(DiagnosticsEngine::Warning, "don't call this superclass method",
263 compat::getBeginLoc(callExpr))
264 << callExpr->getSourceRange();
266 return true;
269 bool XmlImport::VisitBinaryOperator(const BinaryOperator* binaryOp)
271 auto beginLoc = compat::getBeginLoc(binaryOp);
272 if (!beginLoc.isValid() || ignoreLocation(binaryOp))
273 return true;
274 auto op = binaryOp->getOpcode();
275 if (op != BO_EQ && op != BO_NE)
276 return true;
277 auto check2 = [&](const Expr* expr) -> void {
278 if (!isUInt16(expr))
279 report(DiagnosticsEngine::Warning,
280 "comparing XML_TOK enum to 'sal_uInt32', expected sal_uInt16",
281 compat::getBeginLoc(binaryOp))
282 << binaryOp->getSourceRange();
284 if (isXmlTokEnum(binaryOp->getLHS()))
285 check2(binaryOp->getRHS());
286 else if (isXmlTokEnum(binaryOp->getRHS()))
287 check2(binaryOp->getLHS());
288 return true;
291 bool XmlImport::VisitSwitchStmt(const SwitchStmt* switchStmt)
293 auto beginLoc = compat::getBeginLoc(switchStmt);
294 if (!beginLoc.isValid() || ignoreLocation(switchStmt))
295 return true;
296 if (isUInt16(switchStmt->getCond()))
297 return true;
298 // if the condition is an enum type, ignore this switch
299 auto condEnumType = compat::IgnoreImplicit(switchStmt->getCond())
300 ->getType()
301 ->getUnqualifiedDesugaredType()
302 ->getAs<EnumType>();
303 if (condEnumType)
304 return true;
305 auto switchCaseStmt = switchStmt->getSwitchCaseList();
306 for (; switchCaseStmt != nullptr; switchCaseStmt = switchCaseStmt->getNextSwitchCase())
308 auto caseStmt = dyn_cast<CaseStmt>(switchCaseStmt);
309 if (!caseStmt)
310 continue;
311 if (!isXmlTokEnum(caseStmt->getLHS()))
312 continue;
313 report(DiagnosticsEngine::Warning,
314 "comparing XML_TOK enum to 'sal_uInt32', expected sal_uInt16",
315 compat::getBeginLoc(caseStmt))
316 << caseStmt->getSourceRange();
318 return true;
321 bool XmlImport::VisitCallExpr(const CallExpr* callExpr)
323 auto beginLoc = compat::getBeginLoc(callExpr);
324 if (!beginLoc.isValid() || ignoreLocation(callExpr))
325 return true;
327 const FunctionDecl* functionDecl;
328 if (isa<CXXMemberCallExpr>(callExpr))
329 functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl();
330 else
331 functionDecl = callExpr->getDirectCallee();
332 if (!functionDecl)
333 return true;
334 for (unsigned i = 0; i != callExpr->getNumArgs(); ++i)
336 auto argExpr = compat::IgnoreImplicit(callExpr->getArg(i));
337 if (!isXmlTokEnum(argExpr))
338 continue;
339 // if the condition is an enum type, ignore this switch
340 auto condEnumType = functionDecl->getParamDecl(i)
341 ->getType()
342 ->getUnqualifiedDesugaredType()
343 ->getAs<EnumType>();
344 if (condEnumType)
345 continue;
346 if (isUInt16(functionDecl->getParamDecl(i)->getType()))
347 return true;
348 report(DiagnosticsEngine::Warning,
349 "passing XML_TOK enum to 'sal_Int32', wrong param or XML token type",
350 compat::getBeginLoc(callExpr))
351 << callExpr->getSourceRange();
354 return true;
357 bool XmlImport::isXmlTokEnum(const Expr* expr)
359 expr = compat::IgnoreImplicit(expr);
360 // check that we have an unscoped enum type
361 auto condEnumType = expr->getType()->getUnqualifiedDesugaredType()->getAs<EnumType>();
362 if (!condEnumType || condEnumType->getDecl()->isScoped())
363 return false;
364 auto declRefExpr = dyn_cast<DeclRefExpr>(expr);
365 if (!declRefExpr)
366 return false;
367 auto enumConstant = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl());
368 if (!enumConstant)
369 return false;
370 return enumConstant->getIdentifier() && enumConstant->getName().startswith("XML_TOK_");
373 bool XmlImport::isUInt16(const Expr* expr)
375 expr = compat::IgnoreImplicit(expr);
376 return isUInt16(expr->getType());
379 bool XmlImport::isUInt16(QualType qt)
381 if (qt->isSpecificBuiltinType(BuiltinType::UShort))
382 return true;
383 return bool(loplugin::TypeCheck(qt).Typedef("sal_uInt16"));
386 loplugin::Plugin::Registration<XmlImport> xmlimport("xmlimport");
388 } // namespace
390 #endif // LO_CLANG_SHARED_PLUGINS
392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */