/*******************************************************************************
* Copyright (c) 2009, 2011 Alena Laskavaia
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alena Laskavaia - initial API and implementation
* Patrick Hofer [bug 315528]
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers;
import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
/**
* Checker to find that class has virtual method and non virtual destructor
*
* @author Alena Laskavaia
*/
public class NonVirtualDestructor extends AbstractIndexAstChecker {
public static final String PROBLEM_ID = "org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem"; //$NON-NLS-1$
public void processAst(IASTTranslationUnit ast) {
// Traverse the AST using the visitor pattern.
ast.accept(new OnEachClass());
}
private static ICPPMethod getDestructor(ICPPClassType classType) {
for (ICPPMethod method : classType.getDeclaredMethods()) {
if (method.isDestructor()) {
return method;
}
}
return null;
}
private static boolean hasVirtualDestructor(ICPPClassType classType) {
ICPPMethod destructor = getDestructor(classType);
if (destructor != null && destructor.isVirtual()) {
return true;
}
ICPPBase[] bases = classType.getBases();
for (ICPPBase base : bases) {
IBinding baseClass = base.getBaseClass();
if (baseClass instanceof ICPPClassType) {
if (hasVirtualDestructor((ICPPClassType) baseClass)) {
return true;
}
}
}
return false;
}
private class OnEachClass extends ASTVisitor {
OnEachClass() {
shouldVisitDeclSpecifiers = true;
}
public int visit(IASTDeclSpecifier decl) {
if (decl instanceof ICPPASTCompositeTypeSpecifier) {
ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) decl;
IASTName className = spec.getName();
IBinding binding = className.resolveBinding();
if (!(binding instanceof ICPPClassType)) {
return PROCESS_SKIP;
}
ICPPClassType classType = (ICPPClassType) binding;
if (hasVirtualDestructor(classType)) {
return PROCESS_SKIP;
}
ICPPMethod virtualMethod = null;
for (ICPPMethod method : classType.getAllDeclaredMethods()) {
if (!method.isDestructor() && method.isVirtual()) {
virtualMethod = method;
}
}
if (virtualMethod == null) {
return PROCESS_SKIP;
}
ICPPMethod destructor = getDestructor(classType);
if (destructor != null &&
destructor.getVisibility() != ICPPASTVisibilityLabel.v_public &&
classType.getFriends().length == 0) {
// No error if the destructor is protected or private and there are no friends.
return PROCESS_SKIP;
}
IASTNode node = decl;
if (destructor instanceof ICPPInternalBinding) {
IASTNode[] decls = ((ICPPInternalBinding) destructor).getDeclarations();
if (decls != null && decls.length > 0) {
node = decls[0];
}
}
reportProblem(PROBLEM_ID, node, className.getSimpleID().toString(),
virtualMethod.getName());
return PROCESS_SKIP;
}
return PROCESS_CONTINUE;
}
}
}