From f1daf6235aa95f02713dcbd32831f171e5b0af8c Mon Sep 17 00:00:00 2001 From: Andrew Gvozdev Date: Fri, 29 Apr 2011 03:00:09 +0000 Subject: bug 343429: [checker] Checker to pinpoint unused static functions in a file --- .../OSGI-INF/l10n/bundle.properties | 12 + codan/org.eclipse.cdt.codan.checkers/plugin.xml | 655 +++++++++++---------- .../checkers/UnusedSymbolInFileScopeChecker.java | 255 ++++++++ 3 files changed, 612 insertions(+), 310 deletions(-) create mode 100644 codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java (limited to 'codan/org.eclipse.cdt.codan.checkers') diff --git a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties index f94ed6f9e75..cd01cc4b343 100644 --- a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties +++ b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties @@ -111,3 +111,15 @@ checker.name.AbstractClassCreation = Abstract class cannot be instantiated problem.name.AbstractClassCreation = Abstract class cannot be instantiated problem.messagePattern.AbstractClassCreation = The type ''{0}'' must implement the inherited pure virtual method ''{1}'' problem.description.AbstractClassCreation = All inherited pure virtual methods must be implemented to allow instantiation of the class + +checker.name.UnusedSymbolInFileScopeChecker = Unused symbols and declarations in file scope +problem.description.UnusedVariableDeclarationProblem = Finds unused global variable declarations in file scope +problem.messagePattern.UnusedVariableDeclarationProblem = Unused declaration of variable ''{0}'' +problem.name.UnusedVariableDeclarationProblem = Unused variable declaration in file scope +problem.description.UnusedFunctionDeclarationProblem = Finds unused function declarations +problem.messagePattern.UnusedFunctionDeclarationProblem = Unused declaration of function ''{0}'' +problem.name.UnusedFunctionDeclarationProblem = Unused function declaration +problem.description.UnusedStaticFunctionProblem = Finds static functions which cannot be possible used not being referenced inside the file +problem.messagePattern.UnusedStaticFunctionProblem = Unused static function ''{0}'' +problem.name.UnusedStaticFunctionProblem = Unused static function + diff --git a/codan/org.eclipse.cdt.codan.checkers/plugin.xml b/codan/org.eclipse.cdt.codan.checkers/plugin.xml index 0d09d0c6563..f1afe2bd9e6 100644 --- a/codan/org.eclipse.cdt.codan.checkers/plugin.xml +++ b/codan/org.eclipse.cdt.codan.checkers/plugin.xml @@ -9,345 +9,380 @@ class="org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionChecker" id="org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionChecker" name="%checker.name.AssignmentInCondition"> - - + + - - + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + name="%problem.name.UnusedStaticFunctionProblem"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java new file mode 100644 index 00000000000..841b8d45b06 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2011 Andrew Gvozdev and others. + * 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: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.checkers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.codan.checkers.CodanCheckersActivator; +import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker; +import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.IProblemType; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.IVariable; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; + +/** + * Checker looking for unused function or variable declarations. + */ +public class UnusedSymbolInFileScopeChecker extends AbstractIndexAstChecker { + public static final String ER_UNUSED_VARIABLE_DECLARATION_ID = "org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem"; //$NON-NLS-1$ + public static final String ER_UNUSED_FUNCTION_DECLARATION_ID = "org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem"; //$NON-NLS-1$ + public static final String ER_UNUSED_STATIC_FUNCTION_ID = "org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem"; //$NON-NLS-1$ + + private Map externFunctionDeclarations = new HashMap(); + private Map staticFunctionDeclarations = new HashMap(); + private Map staticFunctionDefinitions = new HashMap(); + private Map externVariableDeclarations = new HashMap(); + private Map staticVariableDeclarations = new HashMap(); + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.cdt.codan.core.model.ICheckerWithPreferences#initParameters + * (org.eclipse.cdt.codan.core.model.IProblemWorkingCopy) + */ + @Override + public void initPreferences(IProblemWorkingCopy problem) { + super.initPreferences(problem); + } + + private void clearCandidates() { + externFunctionDeclarations.clear(); + staticFunctionDeclarations.clear(); + staticFunctionDefinitions.clear(); + externVariableDeclarations.clear(); + staticVariableDeclarations.clear(); + } + + private boolean isAnyCandidate() { + return externFunctionDeclarations.size() > 0 || + staticFunctionDeclarations.size() > 0 || + staticFunctionDefinitions.size() > 0 || + externVariableDeclarations.size() > 0 || + staticVariableDeclarations.size() > 0; + } + + public void processAst(IASTTranslationUnit ast) { + if (ast.isHeaderUnit()) + return; + + clearCandidates(); + collectCandidates(ast); + + if (isAnyCandidate()) { + filterOutUsedElements(ast); + reportProblems(); + } + } + + private void collectCandidates(IASTTranslationUnit ast) { + try { + ast.accept(new ASTVisitor() { + { + shouldVisitDeclarations = true; + } + + @Override + public int visit(IASTDeclaration element) { + if (element instanceof IASTSimpleDeclaration) { + // declarations + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) element; + + IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); + for (IASTDeclarator decl : declarators) { + IASTName astName = decl.getName(); + if (astName != null) { + IBinding binding = astName.resolveBinding(); + int storageClass = simpleDeclaration.getDeclSpecifier().getStorageClass(); + + if (binding instanceof IFunction) { + if (storageClass == IASTDeclSpecifier.sc_extern || storageClass == IASTDeclSpecifier.sc_unspecified) { + externFunctionDeclarations.put(binding, decl); + } else if (storageClass == IASTDeclSpecifier.sc_static) { + staticFunctionDeclarations.put(binding, decl); + } + } else if (binding instanceof IVariable) { + if (storageClass == IASTDeclSpecifier.sc_extern) { + externVariableDeclarations.put(binding, decl); + } else if (storageClass == IASTDeclSpecifier.sc_static) { + IType type = ((IVariable) binding).getType(); + // account for class constructor and avoid possible false positive + if (!(type instanceof ICPPClassType) && !(type instanceof IProblemType)) { + staticVariableDeclarations.put(binding, decl); + } + } + } + } + } + return PROCESS_SKIP; + } else if (element instanceof IASTFunctionDefinition) { + // definitions + IASTFunctionDefinition definition = (IASTFunctionDefinition) element; + + IASTName astName = definition.getDeclarator().getName(); + if (astName != null) { + IBinding binding = astName.resolveBinding(); + + if (definition.getDeclSpecifier().getStorageClass() == IASTDeclSpecifier.sc_static) { + if (!(astName instanceof ICPPASTQualifiedName)) { + staticFunctionDefinitions.put(binding, definition.getDeclarator()); + } + } + + // externFunctionDeclarators filter out + externFunctionDeclarations.remove(binding); + // staticFunctionDeclarators filter out + staticFunctionDeclarations.remove(binding); + } + } + return PROCESS_SKIP; + } + + }); + } catch (Exception e) { + CodanCheckersActivator.log(e); + } + } + + private void filterOutUsedElements(IASTTranslationUnit ast) { + try { + ast.accept(new ASTVisitor() { + { + shouldVisitNames = true; + } + + @Override + public int visit(IASTName name) { + IBinding binding = name.resolveBinding(); + if (binding instanceof ICPPMethod) + return PROCESS_CONTINUE; + + IASTNode parentNode = name.getParent(); + + if (!(parentNode instanceof IASTFunctionDefinition || parentNode instanceof IASTFunctionDeclarator)) { + externFunctionDeclarations.remove(binding); + staticFunctionDefinitions.remove(binding); + } + + if (!(parentNode instanceof IASTDeclarator)) { + externVariableDeclarations.remove(binding); + staticVariableDeclarations.remove(binding); + } + + if (!isAnyCandidate()) + return PROCESS_ABORT; + + return PROCESS_CONTINUE; + } + + }); + } catch (Exception e) { + CodanCheckersActivator.log(e); + } + } + + private IASTName getAstName(IASTDeclarator decl) { + IASTName astName = null; + do { + astName = decl.getName(); + if (astName != null && astName.getSimpleID().length > 0) + return astName; + + // resolve parenthesis if need to + decl = decl.getNestedDeclarator(); + } while (decl != null); + + return astName; + } + + private void reportProblems() { + List funcDeclarators = new ArrayList(); + funcDeclarators.addAll(externFunctionDeclarations.values()); + funcDeclarators.addAll(staticFunctionDeclarations.values()); + for (IASTDeclarator symbol : funcDeclarators) { + IASTName astName = getAstName(symbol); + if (astName != null) { + String symbolName = new String(astName.getSimpleID()); + reportProblem(ER_UNUSED_FUNCTION_DECLARATION_ID, astName, symbolName); + } + } + + List varDeclarators = new ArrayList(); + varDeclarators.addAll(externVariableDeclarations.values()); + varDeclarators.addAll(staticVariableDeclarations.values()); + for (IASTDeclarator symbol : varDeclarators) { + IASTName astName = getAstName(symbol); + if (astName != null) { + String symbolName = new String(astName.getSimpleID()); + reportProblem(ER_UNUSED_VARIABLE_DECLARATION_ID, astName, symbolName); + } + } + + List staticFuncDeclarators = new ArrayList(); + staticFuncDeclarators.addAll(staticFunctionDefinitions.values()); + for (IASTDeclarator symbol : staticFuncDeclarators) { + IASTName astName = getAstName(symbol); + if (astName != null) { + String symbolName = new String(astName.getSimpleID()); + reportProblem(ER_UNUSED_STATIC_FUNCTION_ID, astName, symbolName); + } + } + + clearCandidates(); // release memory + } + + @Override + public boolean runInEditor() { + return true; + } +} -- cgit v1.2.3