1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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
18 This is a compile-time checker.
20 Check for cases where we have
21 - two IDL interfaces A and B,
23 - we are converting a Reference<B> to a Reference<A> using UNO_QUERY
25 This makes the code simpler and cheaper, because UNO_QUERY can be surprisingly expensive if used a lot.
31 class ReferenceCasting
: public loplugin::FilteringPlugin
<ReferenceCasting
>
34 explicit ReferenceCasting(loplugin::InstantiationData
const& data
)
35 : FilteringPlugin(data
)
39 bool preRun() override
41 std::string
fn(handler
.getMainFileName());
42 loplugin::normalizeDotDotInFilePath(fn
);
44 if (fn
== SRCDIR
"/dbaccess/source/ui/browser/formadapter.cxx")
53 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
57 bool VisitCXXConstructExpr(const CXXConstructExpr
* cce
);
58 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr
* mce
);
61 bool CheckForUnnecessaryGet(const Expr
*);
64 static const RecordType
* extractTemplateType(const clang::Type
*);
65 static bool isDerivedFrom(const CXXRecordDecl
* subtypeRecord
, const CXXRecordDecl
* baseRecord
);
67 bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr
* cce
)
69 // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
70 StringRef aFileName
= getFilenameOfLocation(
71 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(cce
)));
72 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.h"))
74 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.hxx"))
77 // look for calls to the Reference<T>(x, UNO_something) constructor
78 auto constructorClass
= cce
->getConstructor()->getParent();
79 if (!constructorClass
->getIdentifier() || constructorClass
->getName() != "Reference")
82 if (cce
->getNumArgs() != 2)
85 if (CheckForUnnecessaryGet(cce
->getArg(0)))
86 report(DiagnosticsEngine::Warning
, "unnecessary get() call", compat::getBeginLoc(cce
))
87 << cce
->getSourceRange();
89 // ignore the up-casting constructor
90 if (!isa
<EnumType
>(cce
->getConstructor()->getParamDecl(1)->getType()))
93 // extract the type parameter passed to the template
94 const RecordType
* templateParamType
= extractTemplateType(cce
->getType().getTypePtr());
95 if (!templateParamType
)
98 // extract the type of the first parameter passed to the constructor
99 const Expr
* constructorArg0
= cce
->getArg(0);
100 if (!constructorArg0
)
103 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
104 const clang::Type
* argType
;
107 if (auto castExpr
= dyn_cast
<CastExpr
>(constructorArg0
))
109 constructorArg0
= castExpr
->getSubExpr();
112 if (auto matTempExpr
= dyn_cast
<MaterializeTemporaryExpr
>(constructorArg0
))
114 constructorArg0
= matTempExpr
->GetTemporaryExpr();
117 if (auto bindTempExpr
= dyn_cast
<CXXBindTemporaryExpr
>(constructorArg0
))
119 constructorArg0
= bindTempExpr
->getSubExpr();
122 if (auto tempObjExpr
= dyn_cast
<CXXTemporaryObjectExpr
>(constructorArg0
))
124 constructorArg0
= tempObjExpr
->getArg(0);
127 if (auto parenExpr
= dyn_cast
<ParenExpr
>(constructorArg0
))
129 constructorArg0
= parenExpr
->getSubExpr();
132 argType
= constructorArg0
->getType().getTypePtr();
136 const RecordType
* argTemplateType
= extractTemplateType(argType
);
137 if (!argTemplateType
)
140 CXXRecordDecl
* templateParamRD
= dyn_cast
<CXXRecordDecl
>(templateParamType
->getDecl());
141 CXXRecordDecl
* constructorArgRD
= dyn_cast
<CXXRecordDecl
>(argTemplateType
->getDecl());
143 // querying for XInterface (instead of doing an upcast) has special semantics,
144 // to check for UNO object equivalence.
145 if (templateParamRD
->getName() == "XInterface")
148 // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
149 // can return a completely different object, e.g. see SwXShape::queryInterface
150 if (templateParamRD
->getName() == "XShape")
153 if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(cce
->getArg(1)))
155 // no warning expected, used to reject null references
156 if (auto enumConstantDecl
= dyn_cast
<EnumConstantDecl
>(declRefExpr
->getDecl()))
158 if (enumConstantDecl
->getName() == "UNO_SET_THROW")
160 if (enumConstantDecl
->getName() == "UNO_QUERY_THROW")
162 if (enumConstantDecl
->getName() == "SAL_NO_ACQUIRE")
167 if (constructorArgRD
->Equals(templateParamRD
)
168 || isDerivedFrom(constructorArgRD
, templateParamRD
))
170 report(DiagnosticsEngine::Warning
,
171 "the source reference is already a subtype of the destination reference, just use =",
172 compat::getBeginLoc(cce
))
173 << cce
->getSourceRange();
178 bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr
* mce
)
180 // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
181 StringRef aFileName
= getFilenameOfLocation(
182 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(mce
)));
183 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.h"))
185 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.hxx"))
188 // look for calls to the Reference<T>.set(x, UNO_QUERY) constructor
189 auto method
= mce
->getMethodDecl();
190 if (!method
|| !method
->getIdentifier() || method
->getName() != "set")
192 if (mce
->getNumArgs() != 2)
195 auto methodRecordDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(mce
->getRecordDecl());
196 if (!methodRecordDecl
|| !methodRecordDecl
->getIdentifier()
197 || methodRecordDecl
->getName() != "Reference")
200 if (CheckForUnnecessaryGet(mce
->getArg(0)))
201 report(DiagnosticsEngine::Warning
, "unnecessary get() call", compat::getBeginLoc(mce
))
202 << mce
->getSourceRange();
204 // extract the type parameter passed to the template
205 const RecordType
* templateParamType
206 = dyn_cast
<RecordType
>(methodRecordDecl
->getTemplateArgs()[0].getAsType());
207 if (!templateParamType
)
210 // extract the type of the first parameter passed to the method
211 const Expr
* arg0
= mce
->getArg(0);
215 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
216 const clang::Type
* argType
;
219 if (auto castExpr
= dyn_cast
<CastExpr
>(arg0
))
221 arg0
= castExpr
->getSubExpr();
224 if (auto matTempExpr
= dyn_cast
<MaterializeTemporaryExpr
>(arg0
))
226 arg0
= matTempExpr
->GetTemporaryExpr();
229 if (auto bindTempExpr
= dyn_cast
<CXXBindTemporaryExpr
>(arg0
))
231 arg0
= bindTempExpr
->getSubExpr();
234 if (auto tempObjExpr
= dyn_cast
<CXXTemporaryObjectExpr
>(arg0
))
236 arg0
= tempObjExpr
->getArg(0);
239 if (auto parenExpr
= dyn_cast
<ParenExpr
>(arg0
))
241 arg0
= parenExpr
->getSubExpr();
244 argType
= arg0
->getType().getTypePtr();
248 const RecordType
* argTemplateType
= extractTemplateType(argType
);
249 if (!argTemplateType
)
252 CXXRecordDecl
* templateParamRD
= dyn_cast
<CXXRecordDecl
>(templateParamType
->getDecl());
253 CXXRecordDecl
* methodArgRD
= dyn_cast
<CXXRecordDecl
>(argTemplateType
->getDecl());
255 // querying for XInterface (instead of doing an upcast) has special semantics,
256 // to check for UNO object equivalence.
257 if (templateParamRD
->getName() == "XInterface")
260 // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
261 // can return a completely different object, e.g. see SwXShape::queryInterface
262 if (templateParamRD
->getName() == "XShape")
265 if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(mce
->getArg(1)))
267 // no warning expected, used to reject null references
268 if (auto enumConstantDecl
= dyn_cast
<EnumConstantDecl
>(declRefExpr
->getDecl()))
270 if (enumConstantDecl
->getName() == "UNO_SET_THROW")
272 if (enumConstantDecl
->getName() == "UNO_QUERY_THROW")
274 if (enumConstantDecl
->getName() == "SAL_NO_ACQUIRE")
279 if (methodArgRD
->Equals(templateParamRD
) || isDerivedFrom(methodArgRD
, templateParamRD
))
281 report(DiagnosticsEngine::Warning
,
282 "the source reference is already a subtype of the destination reference, just use =",
283 compat::getBeginLoc(mce
))
284 << mce
->getSourceRange();
291 Reference<T>(x.get(), UNO_QUERY)
292 because sometimes simplifying that means the main purpose of this plugin can kick in.
294 bool ReferenceCasting::CheckForUnnecessaryGet(const Expr
* expr
)
296 expr
= expr
->IgnoreImplicit();
297 auto cxxMemberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(expr
);
298 if (!cxxMemberCallExpr
)
300 auto methodDecl
= cxxMemberCallExpr
->getMethodDecl();
303 if (!methodDecl
->getIdentifier() || methodDecl
->getName() != "get")
306 if (!loplugin::TypeCheck(expr
->getType()).Pointer())
308 if (!loplugin::DeclCheck(methodDecl
->getParent()).Class("Reference").Namespace("uno"))
311 StringRef aFileName
= getFilenameOfLocation(
312 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
)));
313 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/cppu/qa/test_reference.cxx"))
319 static const RecordType
* extractTemplateType(const clang::Type
* cceType
)
321 // check for passing raw pointer to interface case
322 if (cceType
->isPointerType())
324 auto pointeeType
= cceType
->getPointeeType();
325 if (auto elaboratedType
= dyn_cast
<ElaboratedType
>(pointeeType
))
326 pointeeType
= elaboratedType
->desugar();
327 if (auto recordType
= dyn_cast
<RecordType
>(pointeeType
))
331 if (auto elaboratedType
= dyn_cast
<ElaboratedType
>(cceType
))
332 cceType
= elaboratedType
->desugar().getTypePtr();
333 auto cceTST
= dyn_cast
<TemplateSpecializationType
>(cceType
);
336 if (cceTST
->getNumArgs() != 1)
338 const TemplateArgument
& cceTA
= cceTST
->getArg(0);
339 const clang::Type
* templateParamType
= cceTA
.getAsType().getTypePtr();
340 if (auto elaboratedType
= dyn_cast
<ElaboratedType
>(templateParamType
))
341 templateParamType
= elaboratedType
->desugar().getTypePtr();
342 return dyn_cast
<RecordType
>(templateParamType
);
346 Implement my own isDerived because we can't always see all the definitions of the classes involved.
347 which will cause an assert with the normal clang isDerivedFrom code.
349 static bool isDerivedFrom(const CXXRecordDecl
* subtypeRecord
, const CXXRecordDecl
* baseRecord
)
351 // if there is more than one case, then we have an ambiguous conversion, and we can't change the code
352 // to use the upcasting constructor.
353 return loplugin::derivedFromCount(subtypeRecord
, baseRecord
) == 1;
356 loplugin::Plugin::Registration
<ReferenceCasting
> referencecasting("referencecasting");
360 #endif // LO_CLANG_SHARED_PLUGINS
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */