diff options
author | Noel Grandin <noel@peralex.com> | 2016-06-30 14:09:31 +0200 |
---|---|---|
committer | Noel Grandin <noelgrandin@gmail.com> | 2016-07-06 06:38:30 +0000 |
commit | 716844c6ab7cfc18efd61b0f77e285d453b6cc29 (patch) | |
tree | 868d93c4bfd99a84c339cb8e00202fe07d8affb2 /compilerplugins | |
parent | eff871de05c5efdac0d0397b539b3b5e999672c9 (diff) |
restore loplugin:vclwidget checking for calling clear() on VclPtr fields
Change-Id: I85eda1c33016c1461d897fc0a3b70457209a7405
Reviewed-on: https://gerrit.libreoffice.org/26806
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Noel Grandin <noelgrandin@gmail.com>
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/vclwidgets.cxx | 96 |
1 files changed, 96 insertions, 0 deletions
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<const FieldDecl*>& aVclPtrFields, const Stmt *pStmt) +{ + if (!pStmt) + return; + if (isa<CompoundStmt>(pStmt)) { + const CompoundStmt *pCompoundStatement = dyn_cast<CompoundStmt>(pStmt); + for(const Stmt* pStmt : pCompoundStatement->body()) { + findDisposeAndClearStatements(aVclPtrFields, pStmt); + } + return; + } + if (isa<ForStmt>(pStmt)) { + findDisposeAndClearStatements(aVclPtrFields, dyn_cast<ForStmt>(pStmt)->getBody()); + return; + } + if (isa<IfStmt>(pStmt)) { + findDisposeAndClearStatements(aVclPtrFields, dyn_cast<IfStmt>(pStmt)->getThen()); + findDisposeAndClearStatements(aVclPtrFields, dyn_cast<IfStmt>(pStmt)->getElse()); + return; + } + if (!isa<CallExpr>(pStmt)) return; + const CallExpr *pCallExpr = dyn_cast<CallExpr>(pStmt); + + if (!pCallExpr->getDirectCallee()) return; + if (!isa<CXXMethodDecl>(pCallExpr->getDirectCallee())) return; + const CXXMethodDecl *pCalleeMethodDecl = dyn_cast<CXXMethodDecl>(pCallExpr->getDirectCallee()); + if (pCalleeMethodDecl->getNameAsString() != "disposeAndClear" + && pCalleeMethodDecl->getNameAsString() != "clear") + return; + + if (!pCallExpr->getCallee()) return; + + if (!isa<MemberExpr>(pCallExpr->getCallee())) return; + const MemberExpr *pCalleeMemberExpr = dyn_cast<MemberExpr>(pCallExpr->getCallee()); + + if (!pCalleeMemberExpr->getBase()) return; + if (!isa<MemberExpr>(pCalleeMemberExpr->getBase())) return; + const MemberExpr *pCalleeMemberExprBase = dyn_cast<MemberExpr>(pCalleeMemberExpr->getBase()); + + const FieldDecl* xxx = dyn_cast_or_null<FieldDecl>(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<const FieldDecl*> 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<RecordType>(fieldDecl->getType()->getUnqualifiedDesugaredType()); + if (recordType) { + auto d = dyn_cast<ClassTemplateSpecializationDecl>(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; } |