/******************************************************************************* * Copyright (c) 2009 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 *******************************************************************************/ package org.eclipse.cdt.codan.checkers.sample; import java.text.MessageFormat; import org.eclipse.cdt.codan.checkers.Activator; import org.eclipse.cdt.codan.core.model.AbstractIndexAstChecker; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; 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.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 * */ public class NonVirtualDestructor extends AbstractIndexAstChecker { private static final String ER_ID = "org.eclipse.cdt.codan.checkers.sample.NonVirtualDestructorProblem"; public void processAst(IASTTranslationUnit ast) { // traverse the ast using the visitor pattern. ast.accept(new OnEachClass()); } class OnEachClass extends ASTVisitor { private IASTName className; private IBinding virMethodName; private IBinding destName; OnEachClass() { // shouldVisitDeclarations = true; shouldVisitDeclSpecifiers = true; } public int visit(IASTDeclSpecifier decl) { if (isClassDecl(decl)) { try { boolean err = hasErrorCondition(decl); if (err) { String mess; String clazz = className.toString(); String method = virMethodName.getName(); IASTNode ast = decl; if (destName != null) { if (destName instanceof ICPPInternalBinding) { ICPPInternalBinding bin = (ICPPInternalBinding) destName; ast = bin.getDeclarations()[0]; } mess = MessageFormat .format( "Class ''{0}'' has virtual method ''{1}'' but non-virtual destructor ''{2}''", clazz, method, destName.getName()); reportProblem(ER_ID, ast, mess); } } } catch (DOMException e) { // ignore, no error } catch (Exception e) { Activator.log(e); } return PROCESS_SKIP; } return PROCESS_CONTINUE; } /** * @param decl * @throws DOMException */ private boolean hasErrorCondition(IASTDeclSpecifier decl) throws DOMException { ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) decl; className = spec.getName(); IBinding binding = className.getBinding(); if (binding == null) { binding = className.resolveBinding(); } if (binding instanceof ICPPClassType) { ICPPClassType type = (ICPPClassType) binding; virMethodName = null; destName = null; // check for the following conditions: // class has own virtual method and own non-virtual destructor // class has own virtual method and base non-virtual destructor // class has base virtual method and own non-virtual destructor ICPPMethod[] declaredMethods = type.getDeclaredMethods(); boolean hasOwnVirtualMethod = false; boolean hasOwnNonVirDestructor = false; boolean hasDestructor = false; boolean hasVirtualMethod = false; for (int i = 0; i < declaredMethods.length; i++) { ICPPMethod icppMethod = declaredMethods[i]; if (icppMethod.isVirtual() && !icppMethod.isDestructor()) { hasOwnVirtualMethod = true; virMethodName = icppMethod; } if (icppMethod.isDestructor()) { hasDestructor = true; if (!icppMethod.isVirtual()) { hasOwnNonVirDestructor = true; destName = icppMethod; } } } boolean hasVirDestructor = false; // class has own virtual method and own non-virtual destructor if (hasOwnVirtualMethod && hasOwnNonVirDestructor) { return true; } // class does not have virtual methods but has virtual // destructor // - not an error if (hasOwnVirtualMethod == false && hasDestructor == true && hasOwnNonVirDestructor == false) { return false; } ICPPMethod[] allDeclaredMethods = type.getAllDeclaredMethods(); for (int i = 0; i < allDeclaredMethods.length; i++) { ICPPMethod icppMethod = allDeclaredMethods[i]; if (icppMethod.isVirtual() && !icppMethod.isDestructor()) { hasVirtualMethod = true; if (virMethodName == null) virMethodName = icppMethod; } if (icppMethod.isDestructor()) { hasDestructor = true; if (icppMethod.isVirtual()) { hasVirDestructor = true; } else { if (destName == null) destName = icppMethod; } } } if (hasOwnVirtualMethod) { // class has own virtual method and base non-virtual // destructor if (hasDestructor == true && hasVirDestructor == false) { return true; } } else if (hasVirtualMethod) { // class has base virtual method and own non-virtual // destructor if (hasOwnNonVirDestructor == true) { return true; } } } return false; } /** * @param decl * @return */ private boolean isClassDecl(IASTDeclSpecifier decl) { if (decl instanceof ICPPASTCompositeTypeSpecifier) { return true; } return false; } } }