1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * Based on LLVM/Clang.
6  *
7  * This file is distributed under the University of Illinois Open Source
8  * License. See LICENSE.TXT for details.
9  *
10  */
11 #ifndef LO_CLANG_SHARED_PLUGINS
12 
13 #include "plugin.hxx"
14 #include "check.hxx"
15 #include <iostream>
16 
17 /*
18 This is a compile-time checker.
19 
20 Check for cases where we have
21  - two IDL interfaces A and B,
22  - B extends A
23  - we are converting a Reference<B> to a Reference<A> using UNO_QUERY
24 
25 This makes the code simpler and cheaper, because UNO_QUERY can be surprisingly expensive if used a lot.
26 
27 */
28 
29 namespace
30 {
31 class ReferenceCasting : public loplugin::FilteringPlugin<ReferenceCasting>
32 {
33 public:
ReferenceCasting(loplugin::InstantiationData const & data)34     explicit ReferenceCasting(loplugin::InstantiationData const& data)
35         : FilteringPlugin(data)
36     {
37     }
38 
preRun()39     bool preRun() override
40     {
41         std::string fn(handler.getMainFileName());
42         loplugin::normalizeDotDotInFilePath(fn);
43         // macros
44         if (fn == SRCDIR "/dbaccess/source/ui/browser/formadapter.cxx")
45             return false;
46         // UNO aggregation
47         if (fn == SRCDIR "/toolkit/source/controls/stdtabcontroller.cxx")
48             return false;
49         return true;
50     }
51 
run()52     void run() override
53     {
54         if (preRun())
55         {
56             TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
57         }
58     }
59 
60     bool VisitCXXConstructExpr(const CXXConstructExpr* cce);
61     bool VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce);
62 
63 private:
64     bool CheckForUnnecessaryGet(const Expr*);
65 };
66 
67 static const RecordType* extractTemplateType(const clang::Type*);
68 static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
69 
VisitCXXConstructExpr(const CXXConstructExpr * cce)70 bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
71 {
72     // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
73     StringRef aFileName = getFilenameOfLocation(
74         compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(cce)));
75     if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
76         return true;
77     if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
78         return true;
79 
80     // look for calls to the Reference<T>(x, UNO_something) constructor
81     auto constructorClass = cce->getConstructor()->getParent();
82     if (!constructorClass->getIdentifier() || constructorClass->getName() != "Reference")
83         return true;
84 
85     if (cce->getNumArgs() != 2)
86         return true;
87 
88     if (CheckForUnnecessaryGet(cce->getArg(0)))
89         report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(cce))
90             << cce->getSourceRange();
91 
92     // ignore the up-casting constructor
93     if (!isa<EnumType>(cce->getConstructor()->getParamDecl(1)->getType()))
94         return true;
95 
96     // extract the type parameter passed to the template
97     const RecordType* templateParamType = extractTemplateType(cce->getType().getTypePtr());
98     if (!templateParamType)
99         return true;
100 
101     // extract the type of the first parameter passed to the constructor
102     const Expr* constructorArg0 = cce->getArg(0);
103     if (!constructorArg0)
104         return true;
105 
106     // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
107     const clang::Type* argType;
108     for (;;)
109     {
110         if (auto castExpr = dyn_cast<CastExpr>(constructorArg0))
111         {
112             constructorArg0 = castExpr->getSubExpr();
113             continue;
114         }
115         if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(constructorArg0))
116         {
117             constructorArg0 = matTempExpr->GetTemporaryExpr();
118             continue;
119         }
120         if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(constructorArg0))
121         {
122             constructorArg0 = bindTempExpr->getSubExpr();
123             continue;
124         }
125         if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(constructorArg0))
126         {
127             constructorArg0 = tempObjExpr->getArg(0);
128             continue;
129         }
130         if (auto parenExpr = dyn_cast<ParenExpr>(constructorArg0))
131         {
132             constructorArg0 = parenExpr->getSubExpr();
133             continue;
134         }
135         argType = constructorArg0->getType().getTypePtr();
136         break;
137     }
138 
139     const RecordType* argTemplateType = extractTemplateType(argType);
140     if (!argTemplateType)
141         return true;
142 
143     CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
144     CXXRecordDecl* constructorArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
145 
146     // querying for XInterface (instead of doing an upcast) has special semantics,
147     // to check for UNO object equivalence.
148     if (templateParamRD->getName() == "XInterface")
149         return true;
150 
151     // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
152     // can return a completely different object, e.g. see SwXShape::queryInterface
153     if (templateParamRD->getName() == "XShape")
154         return true;
155 
156     if (auto declRefExpr = dyn_cast<DeclRefExpr>(cce->getArg(1)))
157     {
158         // no warning expected, used to reject null references
159         if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
160         {
161             if (enumConstantDecl->getName() == "UNO_SET_THROW")
162                 return true;
163             if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
164                 return true;
165             if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
166                 return true;
167         }
168     }
169 
170     if (constructorArgRD->Equals(templateParamRD)
171         || isDerivedFrom(constructorArgRD, templateParamRD))
172     {
173         report(DiagnosticsEngine::Warning,
174                "the source reference is already a subtype of the destination reference, just use =",
175                compat::getBeginLoc(cce))
176             << cce->getSourceRange();
177     }
178     return true;
179 }
180 
VisitCXXMemberCallExpr(const CXXMemberCallExpr * mce)181 bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
182 {
183     // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
184     StringRef aFileName = getFilenameOfLocation(
185         compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(mce)));
186     if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
187         return true;
188     if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
189         return true;
190 
191     // look for calls to the Reference<T>.set(x, UNO_QUERY) constructor
192     auto method = mce->getMethodDecl();
193     if (!method || !method->getIdentifier() || method->getName() != "set")
194         return true;
195     if (mce->getNumArgs() != 2)
196         return true;
197 
198     auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(mce->getRecordDecl());
199     if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
200         || methodRecordDecl->getName() != "Reference")
201         return true;
202 
203     if (CheckForUnnecessaryGet(mce->getArg(0)))
204         report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(mce))
205             << mce->getSourceRange();
206 
207     // extract the type parameter passed to the template
208     const RecordType* templateParamType
209         = dyn_cast<RecordType>(methodRecordDecl->getTemplateArgs()[0].getAsType());
210     if (!templateParamType)
211         return true;
212 
213     // extract the type of the first parameter passed to the method
214     const Expr* arg0 = mce->getArg(0);
215     if (!arg0)
216         return true;
217 
218     // drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
219     const clang::Type* argType;
220     for (;;)
221     {
222         if (auto castExpr = dyn_cast<CastExpr>(arg0))
223         {
224             arg0 = castExpr->getSubExpr();
225             continue;
226         }
227         if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(arg0))
228         {
229             arg0 = matTempExpr->GetTemporaryExpr();
230             continue;
231         }
232         if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(arg0))
233         {
234             arg0 = bindTempExpr->getSubExpr();
235             continue;
236         }
237         if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(arg0))
238         {
239             arg0 = tempObjExpr->getArg(0);
240             continue;
241         }
242         if (auto parenExpr = dyn_cast<ParenExpr>(arg0))
243         {
244             arg0 = parenExpr->getSubExpr();
245             continue;
246         }
247         argType = arg0->getType().getTypePtr();
248         break;
249     }
250 
251     const RecordType* argTemplateType = extractTemplateType(argType);
252     if (!argTemplateType)
253         return true;
254 
255     CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
256     CXXRecordDecl* methodArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
257 
258     // querying for XInterface (instead of doing an upcast) has special semantics,
259     // to check for UNO object equivalence.
260     if (templateParamRD->getName() == "XInterface")
261         return true;
262 
263     // XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
264     // can return a completely different object, e.g. see SwXShape::queryInterface
265     if (templateParamRD->getName() == "XShape")
266         return true;
267 
268     if (auto declRefExpr = dyn_cast<DeclRefExpr>(mce->getArg(1)))
269     {
270         // no warning expected, used to reject null references
271         if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
272         {
273             if (enumConstantDecl->getName() == "UNO_SET_THROW")
274                 return true;
275             if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
276                 return true;
277             if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
278                 return true;
279         }
280     }
281 
282     if (methodArgRD->Equals(templateParamRD) || isDerivedFrom(methodArgRD, templateParamRD))
283     {
284         report(DiagnosticsEngine::Warning,
285                "the source reference is already a subtype of the destination reference, just use =",
286                compat::getBeginLoc(mce))
287             << mce->getSourceRange();
288     }
289     return true;
290 }
291 
292 /**
293     Check for
294         Reference<T>(x.get(), UNO_QUERY)
295     because sometimes simplifying that means the main purpose of this plugin can kick in.
296  */
CheckForUnnecessaryGet(const Expr * expr)297 bool ReferenceCasting::CheckForUnnecessaryGet(const Expr* expr)
298 {
299     expr = expr->IgnoreImplicit();
300     auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(expr);
301     if (!cxxMemberCallExpr)
302         return false;
303     auto methodDecl = cxxMemberCallExpr->getMethodDecl();
304     if (!methodDecl)
305         return false;
306     if (!methodDecl->getIdentifier() || methodDecl->getName() != "get")
307         return false;
308 
309     if (!loplugin::TypeCheck(expr->getType()).Pointer())
310         return false;
311     if (!loplugin::DeclCheck(methodDecl->getParent()).Class("Reference").Namespace("uno"))
312         return false;
313 
314     StringRef aFileName = getFilenameOfLocation(
315         compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
316     if (loplugin::isSamePathname(aFileName, SRCDIR "/cppu/qa/test_reference.cxx"))
317         return false;
318 
319     return true;
320 }
321 
extractTemplateType(const clang::Type * cceType)322 static const RecordType* extractTemplateType(const clang::Type* cceType)
323 {
324     // check for passing raw pointer to interface case
325     if (cceType->isPointerType())
326     {
327         auto pointeeType = cceType->getPointeeType();
328         if (auto elaboratedType = dyn_cast<ElaboratedType>(pointeeType))
329             pointeeType = elaboratedType->desugar();
330         if (auto recordType = dyn_cast<RecordType>(pointeeType))
331             return recordType;
332     }
333 
334     if (auto elaboratedType = dyn_cast<ElaboratedType>(cceType))
335         cceType = elaboratedType->desugar().getTypePtr();
336     auto cceTST = dyn_cast<TemplateSpecializationType>(cceType);
337     if (!cceTST)
338         return NULL;
339     if (cceTST->getNumArgs() != 1)
340         return NULL;
341     const TemplateArgument& cceTA = cceTST->getArg(0);
342     const clang::Type* templateParamType = cceTA.getAsType().getTypePtr();
343     if (auto elaboratedType = dyn_cast<ElaboratedType>(templateParamType))
344         templateParamType = elaboratedType->desugar().getTypePtr();
345     return dyn_cast<RecordType>(templateParamType);
346 }
347 
348 /**
349   Implement my own isDerived because we can't always see all the definitions of the classes involved.
350   which will cause an assert with the normal clang isDerivedFrom code.
351 */
isDerivedFrom(const CXXRecordDecl * subtypeRecord,const CXXRecordDecl * baseRecord)352 static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord)
353 {
354     // if there is more than one case, then we have an ambiguous conversion, and we can't change the code
355     // to use the upcasting constructor.
356     return loplugin::derivedFromCount(subtypeRecord, baseRecord) == 1;
357 }
358 
359 loplugin::Plugin::Registration<ReferenceCasting> referencecasting("referencecasting");
360 
361 } // namespace
362 
363 #endif // LO_CLANG_SHARED_PLUGINS
364 
365 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
366