From 716844c6ab7cfc18efd61b0f77e285d453b6cc29 Mon Sep 17 00:00:00 2001 From: Noel Grandin Date: Thu, 30 Jun 2016 14:09:31 +0200 Subject: restore loplugin:vclwidget checking for calling clear() on VclPtr fields Change-Id: I85eda1c33016c1461d897fc0a3b70457209a7405 Reviewed-on: https://gerrit.libreoffice.org/26806 Tested-by: Jenkins Reviewed-by: Noel Grandin --- compilerplugins/clang/vclwidgets.cxx | 96 ++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'compilerplugins') diff --git a/compilerplugins/clang/vclwidgets.cxx b/compilerplugins/clang/vclwidgets.cxx index 368c962a84b7..ba0c0073e798 100644 --- a/compilerplugins/clang/vclwidgets.cxx +++ b/compilerplugins/clang/vclwidgets.cxx @@ -12,6 +12,7 @@ #include "plugin.hxx" #include "compat.hxx" +#include "check.hxx" #include "clang/AST/CXXInheritance.h" // Final goal: Checker for VCL widget references. Makes sure that VCL Window subclasses are properly referenced counted and dispose()'ed. @@ -371,6 +372,52 @@ bool VCLWidgets::VisitParmVarDecl(ParmVarDecl const * pvDecl) return true; } + +static void findDisposeAndClearStatements(std::set& aVclPtrFields, const Stmt *pStmt) +{ + if (!pStmt) + return; + if (isa(pStmt)) { + const CompoundStmt *pCompoundStatement = dyn_cast(pStmt); + for(const Stmt* pStmt : pCompoundStatement->body()) { + findDisposeAndClearStatements(aVclPtrFields, pStmt); + } + return; + } + if (isa(pStmt)) { + findDisposeAndClearStatements(aVclPtrFields, dyn_cast(pStmt)->getBody()); + return; + } + if (isa(pStmt)) { + findDisposeAndClearStatements(aVclPtrFields, dyn_cast(pStmt)->getThen()); + findDisposeAndClearStatements(aVclPtrFields, dyn_cast(pStmt)->getElse()); + return; + } + if (!isa(pStmt)) return; + const CallExpr *pCallExpr = dyn_cast(pStmt); + + if (!pCallExpr->getDirectCallee()) return; + if (!isa(pCallExpr->getDirectCallee())) return; + const CXXMethodDecl *pCalleeMethodDecl = dyn_cast(pCallExpr->getDirectCallee()); + if (pCalleeMethodDecl->getNameAsString() != "disposeAndClear" + && pCalleeMethodDecl->getNameAsString() != "clear") + return; + + if (!pCallExpr->getCallee()) return; + + if (!isa(pCallExpr->getCallee())) return; + const MemberExpr *pCalleeMemberExpr = dyn_cast(pCallExpr->getCallee()); + + if (!pCalleeMemberExpr->getBase()) return; + if (!isa(pCalleeMemberExpr->getBase())) return; + const MemberExpr *pCalleeMemberExprBase = dyn_cast(pCalleeMemberExpr->getBase()); + + const FieldDecl* xxx = dyn_cast_or_null(pCalleeMemberExprBase->getMemberDecl()); + if (xxx) + aVclPtrFields.erase(xxx); +} + + bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl ) { if (ignoreLocation(functionDecl)) { @@ -400,6 +447,55 @@ bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl ) } } + // check dispose method to make sure we are actually disposing all of the VclPtr fields + // FIXME this is not exhaustive. We should enable shouldVisitTemplateInstantiations and look deeper inside type declarations + if (pMethodDecl && pMethodDecl->isInstance() && pMethodDecl->getBody() + && pMethodDecl->param_size()==0 + && pMethodDecl->getNameAsString() == "dispose" + && isDerivedFromWindow(pMethodDecl->getParent()) ) + { + std::string methodParent = pMethodDecl->getParent()->getNameAsString(); + if (methodParent == "VirtualDevice" || methodParent == "Breadcrumb") + return true; + + std::set aVclPtrFields; + for(const auto& fieldDecl : pMethodDecl->getParent()->fields()) { + auto const type = loplugin::TypeCheck(fieldDecl->getType()); + if (type.Class("VclPtr").GlobalNamespace()) { + aVclPtrFields.insert(fieldDecl); + } else if (type.Class("vector").StdNamespace() + || type.Class("map").StdNamespace() + || type.Class("list").StdNamespace() + || type.Class("set").StdNamespace()) + { + const RecordType* recordType = dyn_cast_or_null(fieldDecl->getType()->getUnqualifiedDesugaredType()); + if (recordType) { + auto d = dyn_cast(recordType->getDecl()); + if (d && d->getTemplateArgs().size()>0) { + auto const type = loplugin::TypeCheck(d->getTemplateArgs()[0].getAsType()); + if (type.Class("VclPtr").GlobalNamespace()) { + aVclPtrFields.insert(fieldDecl); + } + } + } + } + } + if (!aVclPtrFields.empty()) { + findDisposeAndClearStatements( aVclPtrFields, pMethodDecl->getBody() ); + if (!aVclPtrFields.empty()) { + //pMethodDecl->dump(); + std::string aMessage = BASE_REF_COUNTED_CLASS " subclass dispose() method does not call disposeAndClear() or clear() on the following field(s): "; + for(auto s : aVclPtrFields) + aMessage += ", " + s->getNameAsString(); + report( + DiagnosticsEngine::Warning, + aMessage, + functionDecl->getLocStart()) + << functionDecl->getSourceRange(); + } + } + } + return true; } -- cgit