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  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <string>
11 #include <iostream>
12 
13 #include "plugin.hxx"
14 #include "check.hxx"
15 #include "clang/AST/CXXInheritance.h"
16 
17 // Check for calls to OutputDevice methods that are not passing through RenderContext
18 
19 namespace
20 {
21 
22 class RenderContext:
23     public loplugin::FilteringPlugin<RenderContext>
24 {
25 public:
RenderContext(loplugin::InstantiationData const & data)26     explicit RenderContext(loplugin::InstantiationData const & data):
27         FilteringPlugin(data) {}
28 
run()29     virtual void run() override {
30         TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
31     }
32 
33     bool TraverseFunctionDecl(const FunctionDecl * decl);
34 
35     bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *);
36 
37 private:
38     bool        mbChecking = false;
39 };
40 
41 // We use Traverse to set a flag so we can easily ignore certain method calls
TraverseFunctionDecl(const FunctionDecl * pFunctionDecl)42 bool RenderContext::TraverseFunctionDecl(const FunctionDecl * pFunctionDecl)
43 {
44     if (ignoreLocation(pFunctionDecl)) {
45         return true;
46     }
47     if (!pFunctionDecl->hasBody()) {
48         return true;
49     }
50     if ( pFunctionDecl != pFunctionDecl->getCanonicalDecl() ) {
51         return true;
52     }
53     // Ignore methods inside the OutputDevice class
54     const CXXMethodDecl *pCXXMethodDecl = dyn_cast<CXXMethodDecl>(pFunctionDecl);
55     if (pCXXMethodDecl) {
56         if (loplugin::TypeCheck(pCXXMethodDecl->getParent()).Class("OutputDevice").GlobalNamespace())
57             return true;
58     }
59     // we are only currently interested in methods where the first parameter is RenderContext
60     if (pFunctionDecl->getNumParams() == 0)
61         return true;
62     if ( loplugin::TypeCheck(pFunctionDecl->getParamDecl( 0 )->getType()).Class("RenderContext").GlobalNamespace() ) {
63         return true;
64     }
65     mbChecking = true;
66     TraverseStmt(pFunctionDecl->getBody());
67     mbChecking = false;
68     return true;
69 }
70 
VisitCXXMemberCallExpr(const CXXMemberCallExpr * pCXXMemberCallExpr)71 bool RenderContext::VisitCXXMemberCallExpr(const CXXMemberCallExpr* pCXXMemberCallExpr)
72 {
73     if (!mbChecking)
74         return true;
75     if (ignoreLocation(pCXXMemberCallExpr)) {
76         return true;
77     }
78     const CXXRecordDecl *pCXXRecordDecl = pCXXMemberCallExpr->getRecordDecl();
79     if (!loplugin::TypeCheck(pCXXRecordDecl).Class("OutputDevice").GlobalNamespace()) {
80         return true;
81     }
82     // ignore a handful of methods. They will most probably still be present in Window for use during processing outside of the Paint()
83     // method lifecycle
84     const CXXMethodDecl *pCXXMethodDecl = pCXXMemberCallExpr->getMethodDecl();
85     if (pCXXMethodDecl->isInstance()) {
86         StringRef name = pCXXMethodDecl->getName();
87         if (name == "LogicToPixel" || name == "GetMapMode" || name == "GetFontMetric" || name == "LogicToLogic"
88             || name == "PixelToLogic" || name == "SetDigitLanguage")
89         {
90             return true;
91         }
92     }
93     // for calling through a pointer
94     const ImplicitCastExpr *pImplicitCastExpr = dyn_cast<ImplicitCastExpr>(pCXXMemberCallExpr->getImplicitObjectArgument());
95     if (pImplicitCastExpr) {
96         QualType aType = pImplicitCastExpr->getSubExpr()->getType();
97         if (aType->isPointerType())
98             aType = aType->getPointeeType();
99         std::string t2 = aType.getAsString();
100         if (t2 == "vcl::RenderContext" || t2 == "const vcl::RenderContext")
101             return true;
102     }
103     // for calling through a reference
104     const DeclRefExpr *pDeclRefExpr = dyn_cast<DeclRefExpr>(pCXXMemberCallExpr->getImplicitObjectArgument());
105     if (pDeclRefExpr) {
106         QualType aType = pDeclRefExpr->getType();
107         std::string t2 = aType.getAsString();
108         if (t2 == "vcl::RenderContext" || t2 == "const vcl::RenderContext")
109             return true;
110     }
111     // for calling through a chain of methods
112     const CXXMemberCallExpr *pMemberExpr = dyn_cast<CXXMemberCallExpr>(pCXXMemberCallExpr->getImplicitObjectArgument());
113     if (pMemberExpr) {
114         QualType aType = pMemberExpr->getType();
115         if (aType->isPointerType())
116             aType = aType->getPointeeType();
117         std::string t2 = aType.getAsString();
118         if (t2 == "vcl::RenderContext" || t2 == "const vcl::RenderContext")
119             return true;
120     }
121     report(
122         DiagnosticsEngine::Warning,
123         "Should be calling OutputDevice method through RenderContext.",
124         compat::getBeginLoc(pCXXMemberCallExpr))
125             << pCXXMemberCallExpr->getSourceRange();
126     return true;
127 }
128 
129 loplugin::Plugin::Registration< RenderContext > X("rendercontext", false);
130 
131 }
132 
133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
134