/******************************************************************************* * Copyright (c) 2009, 2012 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) * Marc-Andre Laperle *******************************************************************************/ package org.eclipse.cdt.codan.internal.checkers; import java.util.HashSet; 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.ClassTypeHelper; 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$ // Prevent stack overflow in case: class A: public A {}; private static HashSet<ICPPClassType> checkedClassTypes = new HashSet<ICPPClassType>(); @Override public void processAst(IASTTranslationUnit ast) { // Traverse the AST using the visitor pattern. ast.accept(new OnEachClass()); } private static ICPPMethod getDestructor(ICPPClassType classType, IASTNode point) { for (ICPPMethod method : ClassTypeHelper.getDeclaredMethods(classType, point)) { if (method.isDestructor()) { return method; } } return null; } private static boolean hasVirtualDestructor(ICPPClassType classType, IASTNode point) { checkedClassTypes.add(classType); ICPPMethod destructor = getDestructor(classType, point); if (destructor != null && destructor.isVirtual()) { return true; } ICPPBase[] bases = ClassTypeHelper.getBases(classType, point); for (ICPPBase base : bases) { IBinding baseClass = base.getBaseClass(); if (baseClass instanceof ICPPClassType) { ICPPClassType cppClassType = (ICPPClassType) baseClass; if (!checkedClassTypes.contains(cppClassType) && hasVirtualDestructor(cppClassType, point)) { return true; } } } return false; } private class OnEachClass extends ASTVisitor { OnEachClass() { shouldVisitDeclSpecifiers = true; } @Override 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; boolean hasVirtualDestructor = hasVirtualDestructor(classType, className); checkedClassTypes.clear(); if (hasVirtualDestructor) { return PROCESS_SKIP; } ICPPMethod virtualMethod = null; for (ICPPMethod method : ClassTypeHelper.getAllDeclaredMethods(classType, className)) { if (!method.isDestructor() && method.isVirtual()) { virtualMethod = method; } } if (virtualMethod == null) { return PROCESS_SKIP; } ICPPMethod destructor = getDestructor(classType, className); 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, new String(className.getSimpleID()), virtualMethod.getName()); return PROCESS_SKIP; } return PROCESS_CONTINUE; } } }