loplugin: vclwidgets: ensure that all VclPtr fields are cleared in dispose()
Change-Id: Ie4937e1ae0d79b59ed5d74d4f3d1d135b09270bf
This commit is contained in:
committed by
Michael Meeks
parent
2998905b95
commit
b53f7ee341
@@ -43,6 +43,8 @@ private:
|
|||||||
bool isDisposeCallingSuperclassDispose(const CXXMethodDecl* pMethodDecl);
|
bool isDisposeCallingSuperclassDispose(const CXXMethodDecl* pMethodDecl);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char sVclPtr[] = "VclPtr";
|
||||||
|
|
||||||
bool BaseCheckNotWindowSubclass(const CXXRecordDecl *BaseDefinition, void *) {
|
bool BaseCheckNotWindowSubclass(const CXXRecordDecl *BaseDefinition, void *) {
|
||||||
if (BaseDefinition->getQualifiedNameAsString().compare("vcl::Window") == 0) {
|
if (BaseDefinition->getQualifiedNameAsString().compare("vcl::Window") == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -88,6 +90,10 @@ bool VCLWidgets::VisitCXXDestructorDecl(const CXXDestructorDecl* pCXXDestructorD
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const CXXRecordDecl * pRecordDecl = pCXXDestructorDecl->getParent();
|
const CXXRecordDecl * pRecordDecl = pCXXDestructorDecl->getParent();
|
||||||
|
// ignore vcl::Window class
|
||||||
|
if (pRecordDecl->getQualifiedNameAsString().compare("vcl::Window") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// check if this class is derived from Window
|
// check if this class is derived from Window
|
||||||
if (!isDerivedFromWindow(pRecordDecl)) {
|
if (!isDerivedFromWindow(pRecordDecl)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -96,9 +102,8 @@ bool VCLWidgets::VisitCXXDestructorDecl(const CXXDestructorDecl* pCXXDestructorD
|
|||||||
for(auto fieldDecl : pRecordDecl->fields()) {
|
for(auto fieldDecl : pRecordDecl->fields()) {
|
||||||
const RecordType *pFieldRecordType = fieldDecl->getType()->getAs<RecordType>();
|
const RecordType *pFieldRecordType = fieldDecl->getType()->getAs<RecordType>();
|
||||||
if (pFieldRecordType) {
|
if (pFieldRecordType) {
|
||||||
const CXXRecordDecl *pFieldRecordDecl = dyn_cast<CXXRecordDecl>(pFieldRecordType->getDecl());
|
const CXXRecordDecl *pFieldRecordTypeDecl = dyn_cast<CXXRecordDecl>(pFieldRecordType->getDecl());
|
||||||
static const char sVclPtr[] = "VcllPtr";
|
if (pFieldRecordTypeDecl->getQualifiedNameAsString().compare(0, strlen(sVclPtr), sVclPtr) == 0) {
|
||||||
if (pFieldRecordDecl->getQualifiedNameAsString().compare(0, strlen(sVclPtr), sVclPtr) == 0) {
|
|
||||||
foundVclPtrField = true;
|
foundVclPtrField = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -180,21 +185,23 @@ bool VCLWidgets::VisitFieldDecl(const FieldDecl * fieldDecl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If this field is a VclPtr field, then the class MUST have a dispose method
|
// If this field is a VclPtr field, then the class MUST have a dispose method
|
||||||
static const char sVclPtr[] = "VcllPtr";
|
const CXXRecordDecl *pParentRecordDecl = dyn_cast<CXXRecordDecl>(fieldDecl->getParent());
|
||||||
if (recordDecl->getQualifiedNameAsString().compare(0, strlen(sVclPtr), sVclPtr) == 0) {
|
if (pParentRecordDecl && isDerivedFromWindow(pParentRecordDecl)
|
||||||
|
&& recordDecl->getQualifiedNameAsString().compare(0, strlen(sVclPtr), sVclPtr) == 0)
|
||||||
|
{
|
||||||
bool foundDispose = false;
|
bool foundDispose = false;
|
||||||
for(auto methodDecl : recordDecl->methods()) {
|
for(auto methodDecl : pParentRecordDecl->methods()) {
|
||||||
if (methodDecl->isInstance() && methodDecl->param_size()==0 && methodDecl->getNameAsString() == "dispose") {
|
if (methodDecl->isInstance() && methodDecl->param_size()==0 && methodDecl->getNameAsString() == "dispose") {
|
||||||
foundDispose = true;
|
foundDispose = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!foundDispose) {
|
if (!foundDispose) {
|
||||||
report(
|
report(
|
||||||
DiagnosticsEngine::Warning,
|
DiagnosticsEngine::Warning,
|
||||||
"vcl::Window subclass with a VclPtr field MUST have a dispose() method.",
|
"vcl::Window subclass with a VclPtr field MUST have a dispose() method.",
|
||||||
recordDecl->getLocation())
|
fieldDecl->getLocation())
|
||||||
<< recordDecl->getSourceRange();
|
<< fieldDecl->getSourceRange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +265,47 @@ bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check dispose method to make sure we are actually disposing all of the VclPtr fields
|
||||||
|
if (pMethodDecl && pMethodDecl->isInstance() && pMethodDecl->getBody() && pMethodDecl->param_size()==0
|
||||||
|
&& pMethodDecl->getNameAsString() == "dispose")
|
||||||
|
{
|
||||||
|
std::vector<std::string> aVclPtrFields;
|
||||||
|
for(auto fieldDecl : pMethodDecl->getParent()->fields()) {
|
||||||
|
const RecordType *pFieldRecordType = fieldDecl->getType()->getAs<RecordType>();
|
||||||
|
if (pFieldRecordType) {
|
||||||
|
const CXXRecordDecl *pFieldRecordTypeDecl = dyn_cast<CXXRecordDecl>(pFieldRecordType->getDecl());
|
||||||
|
if (pFieldRecordTypeDecl->getQualifiedNameAsString().compare(0, strlen(sVclPtr), sVclPtr) == 0) {
|
||||||
|
aVclPtrFields.push_back(fieldDecl->getNameAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!aVclPtrFields.empty()) {
|
||||||
|
const CompoundStmt *pCompoundStatement = dyn_cast<CompoundStmt>(pMethodDecl->getBody());
|
||||||
|
for(const Stmt* pStmt : pCompoundStatement->body()) {
|
||||||
|
const CallExpr *pCallExpr = dyn_cast<CallExpr>(pStmt);
|
||||||
|
if (!pCallExpr) continue;
|
||||||
|
if (!pCallExpr->getDirectCallee()) continue;
|
||||||
|
const CXXMethodDecl *pCalleeMethodDecl = dyn_cast<CXXMethodDecl>(pCallExpr->getDirectCallee());
|
||||||
|
if (!pCalleeMethodDecl) continue;
|
||||||
|
if (pCalleeMethodDecl->getNameAsString() != "disposeAndClear") continue;
|
||||||
|
const MemberExpr *pCalleeMemberExpr = dyn_cast<MemberExpr>(pCallExpr->getCallee());
|
||||||
|
if (!pCalleeMemberExpr) continue;
|
||||||
|
const MemberExpr *pCalleeMemberExprBase = dyn_cast<MemberExpr>(pCalleeMemberExpr->getBase());
|
||||||
|
std::string xxx = pCalleeMemberExprBase->getMemberDecl()->getNameAsString();
|
||||||
|
aVclPtrFields.erase(std::remove(aVclPtrFields.begin(), aVclPtrFields.end(), xxx), aVclPtrFields.end());
|
||||||
|
}
|
||||||
|
if (!aVclPtrFields.empty()) {
|
||||||
|
std::string aMessage = "vcl::Window subclass dispose() method does not call disposeAndClear() on the following field(s) ";
|
||||||
|
for(auto s : aVclPtrFields)
|
||||||
|
aMessage += ", " + s;
|
||||||
|
report(
|
||||||
|
DiagnosticsEngine::Warning,
|
||||||
|
aMessage,
|
||||||
|
functionDecl->getBody()->getLocStart())
|
||||||
|
<< functionDecl->getBody()->getSourceRange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user