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)42bool 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)71bool 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