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
19 This is a compile-time checker.
21 Check for cases where we have
22 - two IDL interfaces A and B,
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.
32 class ReferenceCasting
: public loplugin::FilteringPlugin
<ReferenceCasting
>
35 explicit ReferenceCasting(loplugin::InstantiationData
const& data
)
36 : FilteringPlugin(data
)
40 bool preRun() override
42 std::string
fn(handler
.getMainFileName());
43 loplugin::normalizeDotDotInFilePath(fn
);
45 if (fn
== SRCDIR
"/dbaccess/source/ui/browser/formadapter.cxx")
48 if (fn
== SRCDIR
"/toolkit/source/controls/stdtabcontroller.cxx")
57 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
61 bool VisitCXXConstructExpr(const CXXConstructExpr
* cce
);
62 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr
* mce
);
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"))
78 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.hxx"))
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")
86 if (cce
->getNumArgs() != 2)
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()))
97 // extract the type parameter passed to the template
98 const RecordType
* templateParamType
= extractTemplateType(cce
->getType().getTypePtr());
99 if (!templateParamType
)
102 // extract the type of the first parameter passed to the constructor
103 const Expr
* constructorArg0
= cce
->getArg(0);
104 if (!constructorArg0
)
107 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
108 const clang::Type
* argType
;
111 if (auto castExpr
= dyn_cast
<CastExpr
>(constructorArg0
))
113 constructorArg0
= castExpr
->getSubExpr();
116 if (auto matTempExpr
= dyn_cast
<MaterializeTemporaryExpr
>(constructorArg0
))
118 constructorArg0
= compat::getSubExpr(matTempExpr
);
121 if (auto bindTempExpr
= dyn_cast
<CXXBindTemporaryExpr
>(constructorArg0
))
123 constructorArg0
= bindTempExpr
->getSubExpr();
126 if (auto tempObjExpr
= dyn_cast
<CXXTemporaryObjectExpr
>(constructorArg0
))
128 constructorArg0
= tempObjExpr
->getArg(0);
131 if (auto parenExpr
= dyn_cast
<ParenExpr
>(constructorArg0
))
133 constructorArg0
= parenExpr
->getSubExpr();
136 argType
= constructorArg0
->getType().getTypePtr();
140 const RecordType
* argTemplateType
= extractTemplateType(argType
);
141 if (!argTemplateType
)
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")
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")
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")
164 if (enumConstantDecl
->getName() == "UNO_QUERY_THROW")
166 if (enumConstantDecl
->getName() == "SAL_NO_ACQUIRE")
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();
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"))
189 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/com/sun/star/uno/Reference.hxx"))
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")
196 if (mce
->getNumArgs() != 2)
199 auto methodRecordDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(mce
->getRecordDecl());
200 if (!methodRecordDecl
|| !methodRecordDecl
->getIdentifier()
201 || methodRecordDecl
->getName() != "Reference")
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
)
214 // extract the type of the first parameter passed to the method
215 const Expr
* arg0
= mce
->getArg(0);
219 // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
220 const clang::Type
* argType
;
223 if (auto castExpr
= dyn_cast
<CastExpr
>(arg0
))
225 arg0
= castExpr
->getSubExpr();
228 if (auto matTempExpr
= dyn_cast
<MaterializeTemporaryExpr
>(arg0
))
230 arg0
= compat::getSubExpr(matTempExpr
);
233 if (auto bindTempExpr
= dyn_cast
<CXXBindTemporaryExpr
>(arg0
))
235 arg0
= bindTempExpr
->getSubExpr();
238 if (auto tempObjExpr
= dyn_cast
<CXXTemporaryObjectExpr
>(arg0
))
240 arg0
= tempObjExpr
->getArg(0);
243 if (auto parenExpr
= dyn_cast
<ParenExpr
>(arg0
))
245 arg0
= parenExpr
->getSubExpr();
248 argType
= arg0
->getType().getTypePtr();
252 const RecordType
* argTemplateType
= extractTemplateType(argType
);
253 if (!argTemplateType
)
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")
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")
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")
276 if (enumConstantDecl
->getName() == "UNO_QUERY_THROW")
278 if (enumConstantDecl
->getName() == "SAL_NO_ACQUIRE")
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();
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
)
304 auto methodDecl
= cxxMemberCallExpr
->getMethodDecl();
307 if (!methodDecl
->getIdentifier() || methodDecl
->getName() != "get")
310 if (!loplugin::TypeCheck(expr
->getType()).Pointer())
312 if (!loplugin::DeclCheck(methodDecl
->getParent()).Class("Reference").Namespace("uno"))
315 StringRef aFileName
= getFilenameOfLocation(
316 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
)));
317 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/cppu/qa/test_reference.cxx"))
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
))
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
);
354 if (cceTST
->getNumArgs() != 1)
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");
378 #endif // LO_CLANG_SHARED_PLUGINS
380 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */