diff options
-rw-r--r-- | codan/org.eclipse.cdt.codan.checkers/plugin.xml | 12 | ||||
-rw-r--r-- | codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/checkers/sample/NonVirtualDestructor.java | 184 |
2 files changed, 196 insertions, 0 deletions
diff --git a/codan/org.eclipse.cdt.codan.checkers/plugin.xml b/codan/org.eclipse.cdt.codan.checkers/plugin.xml index e526112453b..e2dac3e0d3a 100644 --- a/codan/org.eclipse.cdt.codan.checkers/plugin.xml +++ b/codan/org.eclipse.cdt.codan.checkers/plugin.xml @@ -28,5 +28,17 @@ name="Statement has no effect"> </problem> </checker> + + <checker + class="org.eclipse.cdt.codan.checkers.sample.NonVirtualDestructor" + id="org.eclipse.cdt.codan.checkers.sample.NonVirtualDescructor" + name="NonVirtualDescructorChecker"> + <problem + category="org.eclipse.cdt.codan.core.categories.ProgrammingProblems" + defaultSeverity="Warning" + id="org.eclipse.cdt.codan.checkers.sample.NonVirtualDestructorProblem" + name="Class has a virtual method and non-virtual destructor"> + </problem> + </checker> </extension> </plugin> diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/checkers/sample/NonVirtualDestructor.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/checkers/sample/NonVirtualDestructor.java new file mode 100644 index 00000000000..e481c4f631f --- /dev/null +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/checkers/sample/NonVirtualDestructor.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * 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.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.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTName; +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; +import org.eclipse.core.resources.IFile; + +/** + * 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"; + private IASTName className; + private IBinding virMethodName; + private IBinding destName; + + @Override + public void processAst(IASTTranslationUnit ast) { + // traverse the ast using the visitor pattern. + ast.accept(new OnEachClass()); + } + + class OnEachClass extends ASTVisitor { + 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(); + int line = 1; + IFile file = getFile(); + if (destName != null) { + if (destName instanceof ICPPInternalBinding) { + ICPPInternalBinding bin = (ICPPInternalBinding) destName; + IASTFileLocation fileLocation = bin + .getDeclarations()[0].getFileLocation(); + line = fileLocation.getStartingLineNumber(); + } + mess = MessageFormat + .format( + "Class ''{0}'' has virtual method ''{1}'' but non-virtual destructor ''{2}''", + clazz, method, destName.getName()); + reportProblem(ER_ID, file, line, mess); + } + } + } catch (DOMException e) { + // ignore, no error + } + return PROCESS_SKIP; + } + return PROCESS_CONTINUE; + } + + /** + * @param decl + * @throws DOMException + */ + private boolean hasErrorCondition(IASTDeclSpecifier decl) + throws DOMException { + virMethodName = null; + destName = null; + 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; + // 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 hasOwnDestructor = false; + boolean hasDestructor = false; + boolean hasVirtualMethod = false; + boolean hasNonVirtualDestructor = 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 hasNonVirDestructor = 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 + && hasNonVirDestructor == 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()) { + hasNonVirDestructor = true; + if (destName == null) + destName = icppMethod; + } + } + } + if (hasOwnVirtualMethod) { + // class has own virtual method and base non-virtual + // destructor + if (hasOwnDestructor == false + && hasNonVirtualDestructor == true) { + 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; + } + } +} |