summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorNoel Grandin <noel@peralex.com>2016-06-30 14:09:31 +0200
committerNoel Grandin <noelgrandin@gmail.com>2016-07-06 06:38:30 +0000
commit716844c6ab7cfc18efd61b0f77e285d453b6cc29 (patch)
tree868d93c4bfd99a84c339cb8e00202fe07d8affb2 /compilerplugins
parenteff871de05c5efdac0d0397b539b3b5e999672c9 (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.cxx96
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;
}