blob: 497d56c36ae1e4b9819a3c7a2183977246c6bdf9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Simon Scholz <simon.scholz@vogella.com> - Ongoing maintenance
*******************************************************************************/
package org.eclipse.core.tools.search;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
/*
* Class that removes field declarations which aren't referenced.
*/
public class FindUnusedMembers implements IRunnableWithProgress {
public static interface IResultReporter {
void unusedElementFound(IMember member) throws CoreException;
}
public static class OutputWriter implements IResultReporter {
private final Writer output;
private IType lastType;
public OutputWriter(Writer writer) {
output = writer;
}
@Override
public void unusedElementFound(IMember member) throws CoreException {
try {
if (!member.getDeclaringType().equals(lastType)) {
lastType = member.getDeclaringType();
writeHeader(lastType);
}
if (member instanceof IMethod) {
writeResult((IMethod) member);
} else if (member instanceof IField) {
writeResult((IField) member);
}
} catch (IOException e) {
// to do
}
}
private void writeHeader(IType type) throws IOException {
output.write("\n\n" + type.getFullyQualifiedName()); //$NON-NLS-1$
}
private void writeResult(IField field) throws IOException, JavaModelException {
output.write("\n\t"); //$NON-NLS-1$
output.write(Signature.toString(field.getTypeSignature()));
output.write(" "); //$NON-NLS-1$
output.write(field.getElementName());
}
private void writeResult(IMethod method) throws IOException, JavaModelException {
output.write("\n\t");//$NON-NLS-1$
output.write(Signature.toString(method.getReturnType()));
output.write(" "); //$NON-NLS-1$
output.write(method.getElementName());
output.write("("); //$NON-NLS-1$
String[] types = method.getParameterTypes();
for (int i = 0; i < types.length; i++) {
output.write(Signature.toString(types[i]));
if (i < types.length - 1)
output.write(","); //$NON-NLS-1$
}
output.write(")"); //$NON-NLS-1$
}
}
private final IResultReporter result;
private ICompilationUnit[] units;
protected int unusedMemberCount = 0;
public FindUnusedMembers(ICompilationUnit[] units, Writer writer) {
this(units, new OutputWriter(writer));
}
public FindUnusedMembers(ICompilationUnit[] units, IResultReporter resultReporter) {
this.units = units;
this.result = resultReporter;
}
private void doSearchCU(ICompilationUnit cu, IProgressMonitor monitor) throws CoreException {
IType[] allTypes = cu.getAllTypes();
SubMonitor subMonitor = SubMonitor.convert(monitor, "Processing " + cu.getElementName(), allTypes.length + 1); //$NON-NLS-1$
ASTParser astParser = ASTParser.newParser(AST.JLS8);
astParser.setResolveBindings(true);
astParser.setProject(cu.getJavaProject());
IBinding[] bindings = astParser.createBindings(allTypes, subMonitor.newChild(1));
for (IBinding binding : bindings) {
if (monitor.isCanceled())
throw new OperationCanceledException();
ITypeBinding typeBinding = (ITypeBinding) binding;
subMonitor.setTaskName("Processing '" + typeBinding.getQualifiedName() + "'"); //$NON-NLS-1$//$NON-NLS-2$
doSearchType(typeBinding, subMonitor.newChild(1));
}
}
private boolean methodOverrides(IMethodBinding binding) {
return Bindings.findOverriddenMethod(binding, true) != null;
}
public void doSearchType(ITypeBinding typeBinding, IProgressMonitor monitor) throws CoreException {
IMethodBinding[] methods = typeBinding.getDeclaredMethods();
IVariableBinding[] fields = typeBinding.getDeclaredFields();
SubMonitor subMonitor = SubMonitor.convert(monitor, "Searching for references.", //$NON-NLS-1$
methods.length + fields.length);
for (IMethodBinding methodBinding : methods) {
if (monitor.isCanceled())
throw new OperationCanceledException();
if (methodOverrides(methodBinding))
continue;
IMethod method = (IMethod) methodBinding.getJavaElement();
if (method == null)
continue;
if (hasReferences(method, subMonitor.newChild(1)))
continue;
result.unusedElementFound(method);
unusedMemberCount++;
}
for (IVariableBinding fieldBinding : fields) {
IField field = (IField) fieldBinding.getJavaElement();
if (field == null)
continue;
if (hasReferences(field, subMonitor.split(1)))
continue;
result.unusedElementFound(field);
unusedMemberCount++;
}
if (monitor.isCanceled())
throw new OperationCanceledException();
}
public int getUnusedMethodCount() {
return unusedMemberCount;
}
private boolean hasReferences(IMember member, IProgressMonitor monitor) throws JavaModelException {
final class ReferenceFound extends Error {
private static final long serialVersionUID = 1L;
}
try {
IJavaSearchScope searchScope = RefactoringScopeFactory.create(member.getDeclaringType());
SearchPattern pattern = SearchPattern.createPattern(member, IJavaSearchConstants.REFERENCES);
SearchRequestor requestor = new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
if (match.getAccuracy() == SearchMatch.A_ACCURATE) {
throw new ReferenceFound();
}
}
};
new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
searchScope, requestor, monitor);
} catch (CoreException e) {
throw new JavaModelException(e);
} catch (ReferenceFound e) {
return true;
}
return false;
}
public void process(IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, "Searching unused members", this.units.length); //$NON-NLS-1$
for (ICompilationUnit unit : this.units) {
doSearchCU(unit, subMonitor.split(1));
}
}
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
process(monitor);
} catch (OperationCanceledException e) {
throw new InterruptedException();
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
}