nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / referencecasting.cxx
blobf23e2f6811cdb9f98754ddbfcd7a8545999d0a77
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>
19 This is a compile-time checker.
21 Check for cases where we have
22 - two IDL interfaces A and B,
23 - B extends A
24 - we are converting a Reference<B> to a Reference<A> using UNO_QUERY
26 This makes the code simpler and cheaper, because UNO_QUERY can be surprisingly expensive if used a lot.
30 namespace
32 class ReferenceCasting : public loplugin::FilteringPlugin<ReferenceCasting>
34 public:
35 explicit ReferenceCasting(loplugin::InstantiationData const& data)
36 : FilteringPlugin(data)
40 bool preRun() override
42 std::string fn(handler.getMainFileName());
43 loplugin::normalizeDotDotInFilePath(fn);
44 // macros
45 if (fn == SRCDIR "/dbaccess/source/ui/browser/formadapter.cxx")
46 return false;
47 // UNO aggregation
48 if (fn == SRCDIR "/toolkit/source/controls/stdtabcontroller.cxx")
49 return false;
50 return true;
53 void run() override
55 if (preRun())
57 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
61 bool VisitCXXConstructExpr(const CXXConstructExpr* cce);
62 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce);
64 private:
65 bool CheckForUnnecessaryGet(const Expr*);
68 static const RecordType* extractTemplateType(const clang::Type*);
69 static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
71 bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
73 // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
74 StringRef aFileName = getFilenameOfLocation(
75 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(cce)));
76 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
77 return true;
78 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
79 return true;
81 // look for calls to the Reference<T>(x, UNO_something) constructor
82 auto constructorClass = cce->getConstructor()->getParent();
83 if (!constructorClass->getIdentifier() || constructorClass->getName() != "Reference")
84 return true;
86 if (cce->getNumArgs() != 2)
87 return true;
89 if (CheckForUnnecessaryGet(cce->getArg(0)))
90 report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(cce))
91 << cce->getSourceRange();
93 // ignore the up-casting constructor
94 if (!isa<EnumType>(cce->getConstructor()->getParamDecl(1)->getType()))
95 return true;
97 // extract the type parameter passed to the template
98 const RecordType* templateParamType = extractTemplateType(cce->getType().getTypePtr());
99 if (!templateParamType)
100 return true;
102 // extract the type of the first parameter passed to the constructor
103 const Expr* constructorArg0 = cce->getArg(0);
104 if (!constructorArg0)
105 return true;
107 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
108 const clang::Type* argType;
109 for (;;)
111 if (auto castExpr = dyn_cast<CastExpr>(constructorArg0))
113 constructorArg0 = castExpr->getSubExpr();
114 continue;
116 if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(constructorArg0))
118 constructorArg0 = compat::getSubExpr(matTempExpr);
119 continue;
121 if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(constructorArg0))
123 constructorArg0 = bindTempExpr->getSubExpr();
124 continue;
126 if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(constructorArg0))
128 constructorArg0 = tempObjExpr->getArg(0);
129 continue;
131 if (auto parenExpr = dyn_cast<ParenExpr>(constructorArg0))
133 constructorArg0 = parenExpr->getSubExpr();
134 continue;
136 argType = constructorArg0->getType().getTypePtr();
137 break;
140 const RecordType* argTemplateType = extractTemplateType(argType);
141 if (!argTemplateType)
142 return true;
144 CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
145 CXXRecordDecl* constructorArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
147 // querying for XInterface (instead of doing an upcast) has special semantics,
148 // to check for UNO object equivalence.
149 if (templateParamRD->getName() == "XInterface")
150 return true;
152 // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
153 // can return a completely different object, e.g. see SwXShape::queryInterface
154 if (templateParamRD->getName() == "XShape")
155 return true;
157 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cce->getArg(1)))
159 // no warning expected, used to reject null references
160 if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
162 if (enumConstantDecl->getName() == "UNO_SET_THROW")
163 return true;
164 if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
165 return true;
166 if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
167 return true;
171 if (constructorArgRD->Equals(templateParamRD)
172 || isDerivedFrom(constructorArgRD, templateParamRD))
174 report(DiagnosticsEngine::Warning,
175 "the source reference is already a subtype of the destination reference, just use =",
176 compat::getBeginLoc(cce))
177 << cce->getSourceRange();
179 return true;
182 bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
184 // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
185 StringRef aFileName = getFilenameOfLocation(
186 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(mce)));
187 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
188 return true;
189 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
190 return true;
192 // look for calls to the Reference<T>.set(x, UNO_QUERY) constructor
193 auto method = mce->getMethodDecl();
194 if (!method || !method->getIdentifier() || method->getName() != "set")
195 return true;
196 if (mce->getNumArgs() != 2)
197 return true;
199 auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(mce->getRecordDecl());
200 if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
201 || methodRecordDecl->getName() != "Reference")
202 return true;
204 if (CheckForUnnecessaryGet(mce->getArg(0)))
205 report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(mce))
206 << mce->getSourceRange();
208 // extract the type parameter passed to the template
209 const RecordType* templateParamType
210 = dyn_cast<RecordType>(methodRecordDecl->getTemplateArgs()[0].getAsType());
211 if (!templateParamType)
212 return true;
214 // extract the type of the first parameter passed to the method
215 const Expr* arg0 = mce->getArg(0);
216 if (!arg0)
217 return true;
219 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
220 const clang::Type* argType;
221 for (;;)
223 if (auto castExpr = dyn_cast<CastExpr>(arg0))
225 arg0 = castExpr->getSubExpr();
226 continue;
228 if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(arg0))
230 arg0 = compat::getSubExpr(matTempExpr);
231 continue;
233 if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(arg0))
235 arg0 = bindTempExpr->getSubExpr();
236 continue;
238 if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(arg0))
240 arg0 = tempObjExpr->getArg(0);
241 continue;
243 if (auto parenExpr = dyn_cast<ParenExpr>(arg0))
245 arg0 = parenExpr->getSubExpr();
246 continue;
248 argType = arg0->getType().getTypePtr();
249 break;
252 const RecordType* argTemplateType = extractTemplateType(argType);
253 if (!argTemplateType)
254 return true;
256 CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
257 CXXRecordDecl* methodArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
259 // querying for XInterface (instead of doing an upcast) has special semantics,
260 // to check for UNO object equivalence.
261 if (templateParamRD->getName() == "XInterface")
262 return true;
264 // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
265 // can return a completely different object, e.g. see SwXShape::queryInterface
266 if (templateParamRD->getName() == "XShape")
267 return true;
269 if (auto declRefExpr = dyn_cast<DeclRefExpr>(mce->getArg(1)))
271 // no warning expected, used to reject null references
272 if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
274 if (enumConstantDecl->getName() == "UNO_SET_THROW")
275 return true;
276 if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
277 return true;
278 if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
279 return true;
283 if (methodArgRD->Equals(templateParamRD) || isDerivedFrom(methodArgRD, templateParamRD))
285 report(DiagnosticsEngine::Warning,
286 "the source reference is already a subtype of the destination reference, just use =",
287 compat::getBeginLoc(mce))
288 << mce->getSourceRange();
290 return true;
294 Check for
295 Reference<T>(x.get(), UNO_QUERY)
296 because sometimes simplifying that means the main purpose of this plugin can kick in.
298 bool ReferenceCasting::CheckForUnnecessaryGet(const Expr* expr)
300 expr = expr->IgnoreImplicit();
301 auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(expr);
302 if (!cxxMemberCallExpr)
303 return false;
304 auto methodDecl = cxxMemberCallExpr->getMethodDecl();
305 if (!methodDecl)
306 return false;
307 if (!methodDecl->getIdentifier() || methodDecl->getName() != "get")
308 return false;
310 if (!loplugin::TypeCheck(expr->getType()).Pointer())
311 return false;
312 if (!loplugin::DeclCheck(methodDecl->getParent()).Class("Reference").Namespace("uno"))
313 return false;
315 StringRef aFileName = getFilenameOfLocation(
316 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
317 if (loplugin::isSamePathname(aFileName, SRCDIR "/cppu/qa/test_reference.cxx"))
318 return false;
320 return true;
323 static const RecordType* extractTemplateType(const clang::Type* cceType)
325 // check for passing raw pointer to interface case
326 if (cceType->isPointerType())
328 auto pointeeType = cceType->getPointeeType();
329 if (auto elaboratedType = dyn_cast<ElaboratedType>(pointeeType))
330 pointeeType = elaboratedType->desugar();
331 if (auto recordType = dyn_cast<RecordType>(pointeeType))
332 return recordType;
335 // extract Foo from Reference<Foo>
336 if (auto subst = dyn_cast<SubstTemplateTypeParmType>(cceType))
338 if (auto recType = dyn_cast<RecordType>(subst->desugar().getTypePtr()))
340 if (auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(recType->getDecl()))
342 auto const& args = ctsd->getTemplateArgs();
343 if (args.size() > 0 && args[0].getKind() == TemplateArgument::ArgKind::Type)
344 return dyn_cast_or_null<RecordType>(args[0].getAsType().getTypePtr());
349 if (auto elaboratedType = dyn_cast<ElaboratedType>(cceType))
350 cceType = elaboratedType->desugar().getTypePtr();
351 auto cceTST = dyn_cast<TemplateSpecializationType>(cceType);
352 if (!cceTST)
353 return NULL;
354 if (cceTST->getNumArgs() != 1)
355 return NULL;
356 const TemplateArgument& cceTA = cceTST->getArg(0);
357 const clang::Type* templateParamType = cceTA.getAsType().getTypePtr();
358 if (auto elaboratedType = dyn_cast<ElaboratedType>(templateParamType))
359 templateParamType = elaboratedType->desugar().getTypePtr();
360 return dyn_cast<RecordType>(templateParamType);
364 Implement my own isDerived because we can't always see all the definitions of the classes involved.
365 which will cause an assert with the normal clang isDerivedFrom code.
367 static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord)
369 // if there is more than one case, then we have an ambiguous conversion, and we can't change the code
370 // to use the upcasting constructor.
371 return loplugin::derivedFromCount(subtypeRecord, baseRecord) == 1;
374 loplugin::Plugin::Registration<ReferenceCasting> referencecasting("referencecasting");
376 } // namespace
378 #endif // LO_CLANG_SHARED_PLUGINS
380 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */