LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / xmlimport.cxx
bloba15c3c63f057cfa12636461452210730f2d60735
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 // calling mxSlaveContext
62 if (loplugin::isSamePathname(fn, SRCDIR "/xmloff/source/draw/XMLNumberStyles.cxx"))
63 return false;
64 return true;
67 void run() override
69 if (preRun())
71 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
75 bool VisitCXXMethodDecl(const CXXMethodDecl*);
76 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr*);
77 bool VisitBinaryOperator(const BinaryOperator*);
78 bool VisitSwitchStmt(const SwitchStmt*);
79 bool VisitCallExpr(const CallExpr*);
81 private:
82 bool isXmlTokEnum(const Expr*);
83 bool isUInt16(const Expr*);
84 bool isUInt16(QualType);
86 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> startFastElementSet;
87 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> StartElementSet;
88 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> endFastElementSet;
89 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> EndElementSet;
90 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> charactersSet;
91 std::unordered_map<const CXXRecordDecl*, const CXXMethodDecl*> CharactersSet;
94 bool XmlImport::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
96 auto beginLoc = compat::getBeginLoc(methodDecl);
97 if (!beginLoc.isValid() || ignoreLocation(beginLoc))
98 return true;
100 if (!methodDecl->getIdentifier())
101 return true;
103 auto cxxRecordDecl = methodDecl->getParent();
104 if (!cxxRecordDecl || !cxxRecordDecl->getIdentifier())
105 return true;
107 if (loplugin::DeclCheck(cxxRecordDecl).Class("SvXMLImportContext"))
108 return true;
110 if (!loplugin::isDerivedFrom(cxxRecordDecl, [](Decl const* decl) -> bool {
111 auto const dc = loplugin::DeclCheck(decl);
112 return bool(dc.ClassOrStruct("SvXMLImportContext").GlobalNamespace());
114 return true;
116 auto name = methodDecl->getName();
117 if (name == "startFastElement")
118 startFastElementSet.insert({ cxxRecordDecl, methodDecl });
119 else if (name == "StartElement")
120 StartElementSet.insert({ cxxRecordDecl, methodDecl });
121 else if (name == "endFastElement")
122 endFastElementSet.insert({ cxxRecordDecl, methodDecl });
123 else if (name == "EndElement")
124 EndElementSet.insert({ cxxRecordDecl, methodDecl });
125 else if (name == "characters")
127 if (methodDecl->getNumParams() == 1)
128 charactersSet.insert({ cxxRecordDecl, methodDecl });
130 else if (name == "Characters")
132 if (methodDecl->getNumParams() == 1)
133 CharactersSet.insert({ cxxRecordDecl, methodDecl });
137 auto it1 = endFastElementSet.find(cxxRecordDecl);
138 auto it2 = EndElementSet.find(cxxRecordDecl);
139 if (it1 != endFastElementSet.end() && it2 != EndElementSet.end())
141 auto methodDecl1 = it1->second;
142 report(DiagnosticsEngine::Warning, "cannot override both endFastElement and EndElement",
143 compat::getBeginLoc(methodDecl1))
144 << methodDecl1->getSourceRange();
145 auto methodDecl2 = it2->second;
146 report(DiagnosticsEngine::Warning, "cannot override both endFastElement and EndElement",
147 compat::getBeginLoc(methodDecl2))
148 << methodDecl2->getSourceRange();
153 auto it1 = startFastElementSet.find(cxxRecordDecl);
154 auto it2 = StartElementSet.find(cxxRecordDecl);
155 if (it1 != startFastElementSet.end() && it2 != StartElementSet.end())
157 auto methodDecl1 = it1->second;
158 report(DiagnosticsEngine::Warning,
159 "cannot override both startFastElement and StartElement",
160 compat::getBeginLoc(methodDecl1))
161 << methodDecl1->getSourceRange();
162 auto methodDecl2 = it2->second;
163 report(DiagnosticsEngine::Warning,
164 "cannot override both startFastElement and StartElement",
165 compat::getBeginLoc(methodDecl2))
166 << methodDecl2->getSourceRange();
170 auto it1 = charactersSet.find(cxxRecordDecl);
171 auto it2 = CharactersSet.find(cxxRecordDecl);
172 if (it1 != charactersSet.end() && it2 != CharactersSet.end())
174 auto methodDecl1 = it1->second;
175 report(DiagnosticsEngine::Warning, "cannot override both characters and Characters",
176 compat::getBeginLoc(methodDecl1))
177 << methodDecl1->getSourceRange();
178 auto methodDecl2 = it2->second;
179 report(DiagnosticsEngine::Warning, "cannot override both characters and Characters",
180 compat::getBeginLoc(methodDecl2))
181 << methodDecl2->getSourceRange();
185 auto checkEmpty = [&]() {
186 if (!methodDecl->isThisDeclarationADefinition())
187 return;
188 auto compoundStmt = dyn_cast_or_null<CompoundStmt>(methodDecl->getBody());
189 if (compoundStmt == nullptr || compoundStmt->size() > 0)
190 return;
191 report(DiagnosticsEngine::Warning, "empty, should be removed",
192 compat::getBeginLoc(methodDecl))
193 << methodDecl->getSourceRange();
194 auto canonicalDecl = methodDecl->getCanonicalDecl();
195 if (canonicalDecl != methodDecl)
196 report(DiagnosticsEngine::Note, "definition here", compat::getBeginLoc(canonicalDecl))
197 << canonicalDecl->getSourceRange();
199 auto checkOnlyReturn = [&]() {
200 if (!methodDecl->isThisDeclarationADefinition())
201 return;
202 auto compoundStmt = dyn_cast_or_null<CompoundStmt>(methodDecl->getBody());
203 if (compoundStmt == nullptr || compoundStmt->size() > 1)
204 return;
205 auto returnStmt = dyn_cast_or_null<ReturnStmt>(*compoundStmt->body_begin());
206 if (!returnStmt)
207 return;
208 auto cxxConstructExpr
209 = dyn_cast_or_null<CXXConstructExpr>(returnStmt->getRetValue()->IgnoreImplicit());
210 if (!cxxConstructExpr)
211 return;
212 if (cxxConstructExpr->getNumArgs() != 1)
213 return;
214 if (!isa<CXXNullPtrLiteralExpr>(cxxConstructExpr->getArg(0)->IgnoreImplicit()))
215 return;
216 report(DiagnosticsEngine::Warning, "empty, should be removed",
217 compat::getBeginLoc(methodDecl))
218 << methodDecl->getSourceRange();
219 auto canonicalDecl = methodDecl->getCanonicalDecl();
220 if (canonicalDecl != methodDecl)
221 report(DiagnosticsEngine::Note, "definition here", compat::getBeginLoc(canonicalDecl))
222 << canonicalDecl->getSourceRange();
225 if (name == "startFastElement")
226 checkEmpty();
227 else if (name == "endFastElement")
228 checkEmpty();
229 else if (name == "characters")
230 checkEmpty();
231 else if (name == "createFastChildContext")
232 checkOnlyReturn();
233 else if (name == "createUnknownChildContext")
234 checkOnlyReturn();
236 return true;
239 bool XmlImport::VisitCXXMemberCallExpr(const CXXMemberCallExpr* callExpr)
241 auto beginLoc = compat::getBeginLoc(callExpr);
242 if (!beginLoc.isValid() || ignoreLocation(callExpr))
243 return true;
245 CXXMethodDecl* methodDecl = callExpr->getMethodDecl();
246 if (!methodDecl || !methodDecl->getIdentifier())
247 return true;
249 auto cxxRecordDecl = methodDecl->getParent();
250 if (!cxxRecordDecl || !cxxRecordDecl->getIdentifier())
251 return true;
253 if (!loplugin::DeclCheck(cxxRecordDecl).Class("SvXMLImportContext"))
254 return true;
256 auto name = methodDecl->getName();
257 if (name == "startFastElement" || name == "characters" || name == "endFastElement"
258 || name == "createFastChildContext" || name == "createUnknownChildContext"
259 || name == "StartElement" || name == "EndElement" || name == "Characters"
260 || name == "CreateChildContext")
263 * Calling this superclass method from a subclass method will mess with the fallback logic in the superclass.
265 report(DiagnosticsEngine::Warning, "don't call this superclass method",
266 compat::getBeginLoc(callExpr))
267 << callExpr->getSourceRange();
269 return true;
272 bool XmlImport::VisitBinaryOperator(const BinaryOperator* binaryOp)
274 auto beginLoc = compat::getBeginLoc(binaryOp);
275 if (!beginLoc.isValid() || ignoreLocation(binaryOp))
276 return true;
277 auto op = binaryOp->getOpcode();
278 if (op != BO_EQ && op != BO_NE)
279 return true;
280 auto check2 = [&](const Expr* expr) -> void {
281 if (!isUInt16(expr))
282 report(DiagnosticsEngine::Warning,
283 "comparing XML_TOK enum to 'sal_uInt32', expected sal_uInt16",
284 compat::getBeginLoc(binaryOp))
285 << binaryOp->getSourceRange();
287 if (isXmlTokEnum(binaryOp->getLHS()))
288 check2(binaryOp->getRHS());
289 else if (isXmlTokEnum(binaryOp->getRHS()))
290 check2(binaryOp->getLHS());
291 return true;
294 bool XmlImport::VisitSwitchStmt(const SwitchStmt* switchStmt)
296 auto beginLoc = compat::getBeginLoc(switchStmt);
297 if (!beginLoc.isValid() || ignoreLocation(switchStmt))
298 return true;
299 if (isUInt16(switchStmt->getCond()))
300 return true;
301 // if the condition is an enum type, ignore this switch
302 auto condEnumType = compat::IgnoreImplicit(switchStmt->getCond())
303 ->getType()
304 ->getUnqualifiedDesugaredType()
305 ->getAs<EnumType>();
306 if (condEnumType)
307 return true;
308 auto switchCaseStmt = switchStmt->getSwitchCaseList();
309 for (; switchCaseStmt != nullptr; switchCaseStmt = switchCaseStmt->getNextSwitchCase())
311 auto caseStmt = dyn_cast<CaseStmt>(switchCaseStmt);
312 if (!caseStmt)
313 continue;
314 if (!isXmlTokEnum(caseStmt->getLHS()))
315 continue;
316 report(DiagnosticsEngine::Warning,
317 "comparing XML_TOK enum to 'sal_uInt32', expected sal_uInt16",
318 compat::getBeginLoc(caseStmt))
319 << caseStmt->getSourceRange();
321 return true;
324 bool XmlImport::VisitCallExpr(const CallExpr* callExpr)
326 auto beginLoc = compat::getBeginLoc(callExpr);
327 if (!beginLoc.isValid() || ignoreLocation(callExpr))
328 return true;
330 const FunctionDecl* functionDecl;
331 if (isa<CXXMemberCallExpr>(callExpr))
332 functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl();
333 else
334 functionDecl = callExpr->getDirectCallee();
335 if (!functionDecl)
336 return true;
337 for (unsigned i = 0; i != callExpr->getNumArgs(); ++i)
339 auto argExpr = compat::IgnoreImplicit(callExpr->getArg(i));
340 if (!isXmlTokEnum(argExpr))
341 continue;
342 // if the condition is an enum type, ignore this switch
343 auto condEnumType = functionDecl->getParamDecl(i)
344 ->getType()
345 ->getUnqualifiedDesugaredType()
346 ->getAs<EnumType>();
347 if (condEnumType)
348 continue;
349 if (isUInt16(functionDecl->getParamDecl(i)->getType()))
350 return true;
351 report(DiagnosticsEngine::Warning,
352 "passing XML_TOK enum to 'sal_Int32', wrong param or XML token type",
353 compat::getBeginLoc(callExpr))
354 << callExpr->getSourceRange();
357 return true;
360 bool XmlImport::isXmlTokEnum(const Expr* expr)
362 expr = compat::IgnoreImplicit(expr);
363 // check that we have an unscoped enum type
364 auto condEnumType = expr->getType()->getUnqualifiedDesugaredType()->getAs<EnumType>();
365 if (!condEnumType || condEnumType->getDecl()->isScoped())
366 return false;
367 auto declRefExpr = dyn_cast<DeclRefExpr>(expr);
368 if (!declRefExpr)
369 return false;
370 auto enumConstant = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl());
371 if (!enumConstant)
372 return false;
373 return enumConstant->getIdentifier() && enumConstant->getName().startswith("XML_TOK_");
376 bool XmlImport::isUInt16(const Expr* expr)
378 expr = compat::IgnoreImplicit(expr);
379 return isUInt16(expr->getType());
382 bool XmlImport::isUInt16(QualType qt)
384 if (qt->isSpecificBuiltinType(BuiltinType::UShort))
385 return true;
386 return bool(loplugin::TypeCheck(qt).Typedef("sal_uInt16"));
389 loplugin::Plugin::Registration<XmlImport> xmlimport("xmlimport");
391 } // namespace
393 #endif // LO_CLANG_SHARED_PLUGINS
395 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */