From 739281bec71d6805edb49a057029cbd488f613db Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Mon, 8 Aug 2016 12:01:52 -0700 Subject: [PATCH] Remove Function Bodies and Remove Unused Declarations refactorings. Change-Id: I99cfe7e561ab1615c7d4ee8c126e07acbc672196 --- .../META-INF/MANIFEST.MF | 7 +- .../org.eclipse.cdt.ui.tests/build.properties | 3 +- .../plugin.properties | 16 + core/org.eclipse.cdt.ui.tests/plugin.xml | 45 ++ core/org.eclipse.cdt.ui.tests/pom.xml | 2 +- .../cdt/ui/tests/reducer/Messages.java | 26 + .../cdt/ui/tests/reducer/Messages.properties | 12 + .../cdt/ui/tests/reducer/ProblemFinder.java | 30 ++ .../reducer/RemoveFunctionBodiesHandler.java | 46 ++ .../RemoveFunctionBodiesRefactoring.java | 214 ++++++++ ...RemoveFunctionBodiesRefactoringRunner.java | 36 ++ .../reducer/RemoveFunctionBodiesWizard.java | 28 + .../RemoveUnusedDeclarationsHandler.java | 46 ++ .../RemoveUnusedDeclarationsInputPage.java | 72 +++ .../RemoveUnusedDeclarationsRefactoring.java | 499 ++++++++++++++++++ ...veUnusedDeclarationsRefactoringRunner.java | 36 ++ .../RemoveUnusedDeclarationsWizard.java | 28 + .../ui/refactoring/utils/SelectionHelper.java | 21 +- 18 files changed, 1153 insertions(+), 14 deletions(-) create mode 100644 core/org.eclipse.cdt.ui.tests/plugin.properties create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.properties create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/ProblemFinder.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesHandler.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoring.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoringRunner.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesWizard.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsHandler.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsInputPage.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoringRunner.java create mode 100644 core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsWizard.java diff --git a/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF index 934830e40d1..40b7355ba16 100644 --- a/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF @@ -2,13 +2,15 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.cdt.ui.tests Bundle-SymbolicName: org.eclipse.cdt.ui.tests; singleton:=true -Bundle-Version: 5.4.0.qualifier +Bundle-Version: 5.5.0.qualifier Bundle-Activator: org.eclipse.cdt.ui.testplugin.CTestPlugin +Bundle-Localization: plugin Export-Package: org.eclipse.cdt.ui.testplugin, org.eclipse.cdt.ui.testplugin.util, org.eclipse.cdt.ui.tests, org.eclipse.cdt.ui.tests.DOMAST, org.eclipse.cdt.ui.tests.chelp, + org.eclipse.cdt.ui.tests.reducer, org.eclipse.cdt.ui.tests.refactoring, org.eclipse.cdt.ui.tests.refactoring.rename, org.eclipse.cdt.ui.tests.text, @@ -34,7 +36,8 @@ Require-Bundle: org.eclipse.jface.text, org.eclipse.core.expressions, com.ibm.icu, org.eclipse.ltk.core.refactoring;bundle-version="3.4.0", - org.eclipse.core.filesystem;bundle-version="1.2.0" + org.eclipse.core.filesystem;bundle-version="1.2.0", + org.eclipse.ltk.ui.refactoring Bundle-ActivationPolicy: lazy Bundle-Vendor: Eclipse CDT Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/core/org.eclipse.cdt.ui.tests/build.properties b/core/org.eclipse.cdt.ui.tests/build.properties index 4f4e448e716..6301a24a3be 100644 --- a/core/org.eclipse.cdt.ui.tests/build.properties +++ b/core/org.eclipse.cdt.ui.tests/build.properties @@ -19,5 +19,6 @@ bin.includes = plugin.xml,\ src/,\ ui/,\ META-INF/,\ - .options + .options,\ + plugin.properties src.includes = about.html diff --git a/core/org.eclipse.cdt.ui.tests/plugin.properties b/core/org.eclipse.cdt.ui.tests/plugin.properties new file mode 100644 index 00000000000..0221a51f344 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/plugin.properties @@ -0,0 +1,16 @@ +############################################################################### +# Copyright (c) 2016 Google, Inc 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: +# Sergey Prigogin (Google) - initial implementation +############################################################################### +category.CodeReduction=C++ Code Reduction +command.RemoveFunctionBodies.name=Remove Function Bodies +command.RemoveFunctionBodies.label=Remove &Function Bodies +command.RemoveUnusedDeclarations.name=Remove Unused Declarations +command.RemoveUnusedDeclarations.label=Remove &Unused Declarations +menu.ReduceCode.label=Reduce Code diff --git a/core/org.eclipse.cdt.ui.tests/plugin.xml b/core/org.eclipse.cdt.ui.tests/plugin.xml index 0d2e6d193f1..d5c5d3bf83c 100644 --- a/core/org.eclipse.cdt.ui.tests/plugin.xml +++ b/core/org.eclipse.cdt.ui.tests/plugin.xml @@ -238,4 +238,49 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/org.eclipse.cdt.ui.tests/pom.xml b/core/org.eclipse.cdt.ui.tests/pom.xml index 4e4e7ad243e..133a5789c7a 100644 --- a/core/org.eclipse.cdt.ui.tests/pom.xml +++ b/core/org.eclipse.cdt.ui.tests/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - 5.4.0-SNAPSHOT + 5.5.0-SNAPSHOT org.eclipse.cdt.ui.tests eclipse-test-plugin diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.java new file mode 100644 index 00000000000..320b90f1890 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.osgi.util.NLS; + +final class Messages extends NLS { + public static String RemoveFunctionBodiesRefactoring_RemoveFunctionBodies; + public static String RemoveUnusedDeclarationsRefactoring_RemoveUnusedDeclarations; + + static { + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + // Do not instantiate. + private Messages() { + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.properties b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.properties new file mode 100644 index 00000000000..de5fc37d106 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/Messages.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 2016 Google, Inc 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: +# Sergey Prigogin (Google) - initial implementation +############################################################################### +RemoveFunctionBodiesRefactoring_RemoveFunctionBodies=Remove Function Bodies +RemoveUnusedDeclarationsRefactoring_RemoveUnusedDeclarations=Remove Unused Declarations \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/ProblemFinder.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/ProblemFinder.java new file mode 100644 index 00000000000..c4e123ca056 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/ProblemFinder.java @@ -0,0 +1,30 @@ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; + +class ProblemFinder extends ASTVisitor { + boolean foundProblem; + + public ProblemFinder() { + shouldVisitNames = true; + shouldVisitImplicitNames = true; + } + + public boolean containsProblemBinding(IASTNode node) { + foundProblem = false; + node.accept(this); + return foundProblem; + } + + @Override + public int visit(IASTName name) { + if (name.resolveBinding() instanceof IProblemBinding) { + foundProblem = true; + return PROCESS_ABORT; + } + return PROCESS_CONTINUE; + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesHandler.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesHandler.java new file mode 100644 index 00000000000..0cf0f5b06ab --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesHandler.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.handlers.HandlerUtil; + +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.ICEditor; + +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; + +public class RemoveFunctionBodiesHandler extends AbstractHandler { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof ITextSelection) { + IWorkbenchPart part = HandlerUtil.getActivePart(event); + if (part instanceof ICEditor) { + ICEditor editor = (ICEditor) part; + IWorkingCopy wc = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); + if (wc != null && wc.getResource() != null) { + RefactoringRunner runner = new RemoveFunctionBodiesRefactoringRunner(wc, selection, + editor.getEditorSite(), wc.getCProject()); + runner.run(); + } + } + } + + return null; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoring.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoring.java new file mode 100644 index 00000000000..8a72e70f6ee --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoring.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; + +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.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.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.INodeFactory; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.formatter.DefaultCodeFormatterOptions; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.ui.refactoring.CTextFileChange; + +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding; +import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.changes.CCompositeChange; + +public class RemoveFunctionBodiesRefactoring extends CRefactoring { + private INodeFactory nodeFactory; + private final DefaultCodeFormatterOptions formattingOptions; + + private IIndex index; + private IASTTranslationUnit ast; + + public RemoveFunctionBodiesRefactoring(ICElement element, ISelection selection, ICProject project) { + super(element, selection, project); + name = Messages.RemoveFunctionBodiesRefactoring_RemoveFunctionBodies; + formattingOptions = new DefaultCodeFormatterOptions(project.getOptions(true)); + } + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + SubMonitor progress = SubMonitor.convert(pm, 10); + + RefactoringStatus status = super.checkInitialConditions(progress.newChild(8)); + if (status.hasError()) { + return status; + } + + ast = getAST(tu, progress.newChild(1)); + index = getIndex(); + nodeFactory = ast.getASTNodeFactory(); + + if (isProgressMonitorCanceled(progress, initStatus)) + return initStatus; + + return initStatus; + } + + private ICPPASTFunctionDeclarator getDeclaration(IASTNode node) { + while (node != null && !(node instanceof IASTFunctionDefinition)) { + node = node.getParent(); + } + if (node != null) { + IASTFunctionDeclarator declarator = ((IASTFunctionDefinition) node).getDeclarator(); + if (declarator instanceof ICPPASTFunctionDeclarator) { + return (ICPPASTFunctionDeclarator) declarator; + } + } + return null; + } + + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext checkContext) { + return new RefactoringStatus(); + } + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + // This method bypasses the standard refactoring framework involving ModificationCollector and ASTRewrite since + // it is too slow for the gigantic changes this refactoring has to deal with. + FunctionDefinitionCollector finder = new FunctionDefinitionCollector(); + ast.accept(finder); + String code = ast.getRawSignature(); + CTextFileChange fileChange = new CTextFileChange(tu.getElementName(), tu); + fileChange.setEdit(new MultiTextEdit()); + for (IASTFunctionDefinition definition : finder.functionDefinitions) { + IASTStatement body = definition.getBody(); + IASTName name = definition.getDeclarator().getName(); + IBinding binding = name.resolveBinding(); + if (binding instanceof ICPPInternalBinding) { + IASTNode[] declarations = ((ICPPInternalBinding) binding).getDeclarations(); + if (declarations != null && declarations.length != 0 + && ((ASTNode) declarations[0]).getOffset() < ((ASTNode) definition).getOffset()) { + IASTNode node = definition; + IASTNode parent; + while ((parent = node.getParent()) instanceof ICPPASTTemplateDeclaration) { + node = parent; + } + int offset = ASTNodes.offset(node); + int endOffset = ASTNodes.endOffset(node); + offset = skipWhitespaceBefore(offset, code); + // Remove the whole definition since the function is declared already. + fileChange.addEdit(new DeleteEdit(offset, endOffset - offset)); + continue; + } + } + + int offset = ASTNodes.offset(body); + int endOffset = ASTNodes.endOffset(body); + if (definition instanceof ICPPASTFunctionDefinition) { + ICPPASTConstructorChainInitializer[] initializers = + ((ICPPASTFunctionDefinition) definition).getMemberInitializers(); + if (initializers.length != 0) { + offset = ASTNodes.offset(initializers[0]); + offset = skipWhitespaceBefore(offset, code); + if (offset > 0 && code.charAt(offset - 1) == ':') + offset--; + } + } + offset = skipWhitespaceBefore(offset, code); + fileChange.addEdit(new ReplaceEdit(offset, endOffset - offset, ";")); + } + + CCompositeChange change = new CCompositeChange(""); //$NON-NLS-1$ + change.markAsSynthetic(); + change.add(fileChange); + change.setDescription(new RefactoringChangeDescriptor(getRefactoringDescriptor())); + return change; + } + + private static int skipWhitespaceBefore(int offset, String text) { + while (--offset >= 0) { + char c = text.charAt(offset); + if (!Character.isWhitespace(c)) + break; + } + return offset + 1; + } + + @Override + protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) + throws CoreException, OperationCanceledException { + // This method is no-op for this refactoring. The change is created in the createChange method. + } + + /** + * Finds function definitions that have bodies, are not constexpr, and don't contain problem bindings. + */ + private class FunctionDefinitionCollector extends ASTVisitor { + final List functionDefinitions = new ArrayList<>(); + final ProblemFinder problemFinder = new ProblemFinder(); + + FunctionDefinitionCollector() { + shouldVisitDeclarations = true; + } + + @Override + public int visit(IASTDeclaration declaration) { + if (!declaration.isPartOfTranslationUnitFile()) + return PROCESS_SKIP; + if (!(declaration instanceof IASTFunctionDefinition)) + return PROCESS_CONTINUE; + IASTFunctionDefinition definition = (IASTFunctionDefinition) declaration; + if (definition.getBody() == null) + return PROCESS_SKIP; + IASTDeclSpecifier declSpec = definition.getDeclSpecifier(); + if (declSpec instanceof ICPPASTDeclSpecifier && ((ICPPASTDeclSpecifier) declSpec).isConstexpr()) + return PROCESS_SKIP; + if (problemFinder.containsProblemBinding(declaration)) + return PROCESS_SKIP; + functionDefinitions.add(definition); + return PROCESS_SKIP; + } + } + + @Override + protected RefactoringDescriptor getRefactoringDescriptor() { + return null; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoringRunner.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoringRunner.java new file mode 100644 index 00000000000..10d3e917b12 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesRefactoringRunner.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.window.IShellProvider; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; + +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; +import org.eclipse.cdt.internal.ui.refactoring.RefactoringSaveHelper; + +public class RemoveFunctionBodiesRefactoringRunner extends RefactoringRunner { + + public RemoveFunctionBodiesRefactoringRunner(ICElement element, ISelection selection, + IShellProvider shellProvider, ICProject cProject) { + super(element, selection, shellProvider, cProject); + } + + @Override + public void run() { + RemoveFunctionBodiesRefactoring refactoring = + new RemoveFunctionBodiesRefactoring(element, selection, project); + RemoveFunctionBodiesWizard wizard = new RemoveFunctionBodiesWizard(refactoring); + run(wizard, refactoring, RefactoringSaveHelper.SAVE_NOTHING); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesWizard.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesWizard.java new file mode 100644 index 00000000000..39d1084e67c --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveFunctionBodiesWizard.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +import org.eclipse.cdt.ui.CUIPlugin; + +public class RemoveFunctionBodiesWizard extends RefactoringWizard { + public RemoveFunctionBodiesWizard(RemoveFunctionBodiesRefactoring refactoring) { + super(refactoring, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); + setDefaultPageTitle(Messages.RemoveFunctionBodiesRefactoring_RemoveFunctionBodies); + setDialogSettings(CUIPlugin.getDefault().getDialogSettings()); + } + + @Override + protected void addUserInputPages() { + } +} + diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsHandler.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsHandler.java new file mode 100644 index 00000000000..a2480224e52 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsHandler.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.handlers.HandlerUtil; + +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.ICEditor; + +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; + +public class RemoveUnusedDeclarationsHandler extends AbstractHandler { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof ITextSelection) { + IWorkbenchPart part = HandlerUtil.getActivePart(event); + if (part instanceof ICEditor) { + ICEditor editor = (ICEditor) part; + IWorkingCopy wc = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); + if (wc != null && wc.getResource() != null) { + RefactoringRunner runner = new RemoveUnusedDeclarationsRefactoringRunner(wc, selection, + editor.getEditorSite(), wc.getCProject()); + runner.run(); + } + } + } + + return null; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsInputPage.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsInputPage.java new file mode 100644 index 00000000000..f28ce8a43aa --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsInputPage.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.cdt.internal.ui.CPluginImages; +import org.eclipse.cdt.internal.ui.dialogs.TextFieldNavigationHandler; +import org.eclipse.cdt.internal.ui.util.RowLayouter; + +public class RemoveUnusedDeclarationsInputPage extends UserInputWizardPage { + public static final String PAGE_NAME = "RemoveUnusedDeclarationsInputPage"; //$NON-NLS-1$ + + private RemoveFunctionBodiesRefactoring refactoring; + private Text textField; + private IDialogSettings settings; + + public RemoveUnusedDeclarationsInputPage() { + super(PAGE_NAME); + setImageDescriptor(CPluginImages.DESC_WIZBAN_REFACTOR_TU); + } + + @Override + public void createControl(Composite parent) { + refactoring = (RemoveFunctionBodiesRefactoring) getRefactoring(); + + Composite result = new Composite(parent, SWT.NONE); + setControl(result); + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + result.setLayout(layout); + RowLayouter layouter = new RowLayouter(2); + GridData gd = null; + + initializeDialogUnits(result); + + Composite group = new Composite(result, SWT.NONE); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + layout = new GridLayout(); + layout.numColumns = 4; + layout.marginWidth = 0; + group.setLayout(layout); + } + + private Text createTextInputField(Composite parent, int style) { + Text result = new Text(parent, style); + result.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { +// textModified(result.getText()); + } + }); + TextFieldNavigationHandler.install(result); + return result; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java new file mode 100644 index 00000000000..d212dd4bdc1 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java @@ -0,0 +1,499 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import static org.eclipse.cdt.internal.core.dom.parser.ASTQueries.findInnermostDeclarator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; + +import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTDeclarationListOwner; +import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier; +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.IASTPreprocessorMacroExpansion; +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.INodeFactory; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; +import org.eclipse.cdt.core.formatter.DefaultCodeFormatterOptions; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.parser.util.CharArrayUtils; +import org.eclipse.cdt.ui.refactoring.CTextFileChange; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding; +import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.changes.CCompositeChange; +import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper; + +public class RemoveUnusedDeclarationsRefactoring extends CRefactoring { + private static final IASTName UNUSED_NAME = new CPPASTName(null); + private static final ProblemFinder problemFinder = new ProblemFinder(); + + private INodeFactory nodeFactory; + private final DefaultCodeFormatterOptions formattingOptions; + + private IIndex index; + private IASTTranslationUnit ast; + private IRegion region; + + public RemoveUnusedDeclarationsRefactoring(ICElement element, ISelection selection, ICProject project) { + super(element, selection, project); + name = Messages.RemoveUnusedDeclarationsRefactoring_RemoveUnusedDeclarations; + formattingOptions = new DefaultCodeFormatterOptions(project.getOptions(true)); + } + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + SubMonitor progress = SubMonitor.convert(pm, 10); + + RefactoringStatus status = super.checkInitialConditions(progress.newChild(8)); + if (status.hasError()) { + return status; + } + + ast = getAST(tu, progress.newChild(1)); + index = getIndex(); + nodeFactory = ast.getASTNodeFactory(); + region = selectedRegion.getLength() == 0 ? + new Region(0, ast.getFileLocation().getNodeLength()) : selectedRegion; + + if (isProgressMonitorCanceled(progress, initStatus)) + return initStatus; + + return initStatus; + } + + private ICPPASTFunctionDeclarator getDeclaration(IASTNode node) { + while (node != null && !(node instanceof IASTFunctionDefinition)) { + node = node.getParent(); + } + if (node != null) { + IASTFunctionDeclarator declarator = ((IASTFunctionDefinition) node).getDeclarator(); + if (declarator instanceof ICPPASTFunctionDeclarator) { + return (ICPPASTFunctionDeclarator) declarator; + } + } + return null; + } + + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext checkContext) { + return new RefactoringStatus(); + } + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + // This method bypasses the standard refactoring framework involving ModificationCollector and ASTRewrite since + // it is too slow for the gigantic changes this refactoring has to deal with. + NavigableSet names = NameCollector.getContainedNames(ast); + + SortedNodeSet nodesToDelete = new SortedNodeSet<>(); + IASTPreprocessorMacroExpansion[] macroExpansions = ast.getMacroExpansions(); + for (IASTPreprocessorMacroExpansion macroExpansion : macroExpansions) { + IASTName name = macroExpansion.getMacroReference(); + if (SelectionHelper.isNodeInsideRegion(name, region) + && macroExpansion.getMacroDefinition().getExpansion().isEmpty()) { + nodesToDelete.add(macroExpansion); + } + } + + CandidateDeclarationFinder finder = new CandidateDeclarationFinder(); + ast.accept(finder); + List declarations = finder.declarations; + + for (int i = declarations.size(); --i >= 0;) { + IASTDeclaration declaration = declarations.get(i); + if (SelectionHelper.isNodeInsideRegion(declaration, region) + && !problemFinder.containsProblemBinding(declaration) + && !isPossiblyUsed(declaration, names, nodesToDelete)) { + nodesToDelete.add(declaration); + removeContainedNames(declaration, names); + } + } + + String code = ast.getRawSignature(); + CTextFileChange fileChange = new CTextFileChange(tu.getElementName(), tu); + fileChange.setEdit(new MultiTextEdit()); + + int maxOffset = 0; + TextEdit lastEdit = null; + IASTNode lastNode = null; + for (IASTNode node : nodesToDelete) { + int offset = ASTNodes.offset(node); + int endOffset = ASTNodes.endOffset(node); + offset = skipWhitespaceBefore(offset, code); + if (offset < region.getOffset()) + offset = region.getOffset(); + // Do not attempt to delete nodes inside a deleted region. + if (endOffset > maxOffset) { + DeleteEdit edit = new DeleteEdit(offset, endOffset - offset); + fileChange.addEdit(edit); + if (maxOffset < endOffset) + maxOffset = endOffset; + lastEdit = edit; + lastNode = node; + } + } + + CCompositeChange change = new CCompositeChange(""); //$NON-NLS-1$ + change.markAsSynthetic(); + change.add(fileChange); + change.setDescription(new RefactoringChangeDescriptor(getRefactoringDescriptor())); + return change; + } + + private boolean containsAncestor(Collection nodes, IASTNode node) { + while ((node = node.getParent()) != null) { + if (nodes.contains(node)) + return true; + } + return false; + } + + private IASTNode getTopMostContainer(IASTNode node) { + while (node != null) { + IASTNode prevNode = node; + node = node.getParent(); + if (node instanceof IASTTranslationUnit) + return prevNode; + } + return null; + } + + private boolean isPossiblyUsed(IASTDeclaration declaration, NavigableSet names, + SortedNodeSet nodesToDelete) { + if (declaration instanceof ICPPASTNamespaceDefinition) { + // An empty namespace definition can be removed. + IASTDeclaration[] children = ((ICPPASTNamespaceDefinition) declaration).getDeclarations(false); + for (IASTDeclaration child : children) { + if (!nodesToDelete.contains(child)) + return true; + } + return false; + } else if (declaration instanceof ICPPASTLinkageSpecification) { + // An empty linkage specification can be removed. + IASTDeclaration[] children = ((ICPPASTLinkageSpecification) declaration).getDeclarations(false); + for (IASTDeclaration child : children) { + if (!nodesToDelete.contains(child)) + return true; + } + return false; + } else if (declaration instanceof ICPPASTVisibilityLabel) { + // A visibility label not followed by a member declaration can be removed. + IASTNode parent = declaration.getParent(); + IASTDeclaration[] siblings = ((ICPPASTCompositeTypeSpecifier) parent).getDeclarations(false); + boolean after = false; + for (IASTDeclaration sibling : siblings) { + if (after) { + if (sibling instanceof ICPPASTVisibilityLabel) + break; + if (!nodesToDelete.contains(sibling)) + return true; + } else if (sibling == declaration) { + after = true; + } + } + return false; + } + + Collection declaredNames = getDeclaredNames(declaration); + if (declaredNames == null) + return true; + + for (IASTName declName : declaredNames) { + char[] declNameChars = declName.getSimpleID(); + if (declNameChars.length != 0 && declNameChars[0] == '~') + declNameChars = Arrays.copyOfRange(declNameChars, 1, declNameChars.length); + IASTNode startPoint = declName; + int startOffset = ASTNodes.endOffset(declaration); + if (declaration.getPropertyInParent() == IASTCompositeTypeSpecifier.MEMBER_DECLARATION) { + // Member declarations can be referenced by other members declared before them. + startPoint = declaration.getParent(); + startOffset = ASTNodes.offset(startPoint); + } else { + ASTNodeProperty property = declName.getPropertyInParent(); + if (property == IASTCompositeTypeSpecifier.TYPE_NAME + || property == ICPPASTEnumerationSpecifier.ENUMERATION_NAME && ((ICPPASTEnumerationSpecifier) declName.getParent()).isScoped()) { + while (declName instanceof ICPPASTTemplateId) { + declName = ((ICPPASTTemplateId) declName).getTemplateName(); + } + IBinding binding = declName.resolveBinding(); + if (binding instanceof IProblemBinding) + return true; + if (binding instanceof ICPPInternalBinding) { + IASTNode[] declarations = ((ICPPInternalBinding) binding).getDeclarations(); + if (declarations != null && declarations.length != 0) { + IASTNode firstDeclaration = declarations[0]; + int firstDeclarationOffset = ASTNodes.offset(firstDeclaration); + if (startOffset > firstDeclarationOffset) { + startPoint = firstDeclaration; + startOffset = firstDeclarationOffset; + } + } + } + } + } + + for (IASTName name : names) { + if (name != declName) { + char[] nameChars = name.getSimpleID(); + int offset = nameChars.length != 0 && nameChars[0] == '~' ? 1 : 0; + if (CharArrayUtils.equals(nameChars, offset, nameChars.length - offset, declNameChars) + && (ASTNodes.offset(name) >= startOffset + || isInsideTemplateDeclarationOrSpecialization(name))) { + return true; + } + } + } + } + return false; + }; + + private static Collection getDeclaredNames(IASTDeclaration declaration) { + while (declaration instanceof ICPPASTTemplateDeclaration) { + declaration = ((ICPPASTTemplateDeclaration) declaration).getDeclaration(); + } + while (declaration instanceof ICPPASTTemplateSpecialization) { + declaration = ((ICPPASTTemplateSpecialization) declaration).getDeclaration(); + } + while (declaration instanceof ICPPASTExplicitTemplateInstantiation) { + declaration = ((ICPPASTExplicitTemplateInstantiation) declaration).getDeclaration(); + } + + if (declaration instanceof IASTSimpleDeclaration) { + List names = new ArrayList<>(); + IASTDeclarator[] declarators = ((IASTSimpleDeclaration) declaration).getDeclarators(); + for (IASTDeclarator declarator : declarators) { + declarator = findInnermostDeclarator(declarator); + IASTName name = declarator.getName(); + if (name instanceof ICPPASTConversionName) + return null; // Do not remove conversion operators. + names.add(name); + } + IASTDeclSpecifier declSpecifier = ((IASTSimpleDeclaration) declaration).getDeclSpecifier(); + if (declSpecifier instanceof IASTCompositeTypeSpecifier) { + names.add(((IASTCompositeTypeSpecifier) declSpecifier).getName()); + } else if (declSpecifier instanceof IASTElaboratedTypeSpecifier) { + names.add(((IASTElaboratedTypeSpecifier) declSpecifier).getName()); + } else if (declSpecifier instanceof IASTEnumerationSpecifier) { + names.add(((IASTEnumerationSpecifier) declSpecifier).getName()); + } + return names; + } else if (declaration instanceof IASTFunctionDefinition) { + IASTDeclarator declarator = ((IASTFunctionDefinition) declaration).getDeclarator(); + declarator = findInnermostDeclarator(declarator); + IASTName name = declarator.getName(); + if (name instanceof ICPPASTConversionName) + return null; // Do not remove conversion operators. + return Collections.singletonList(name); + } else if (declaration instanceof ICPPASTUsingDirective) { + return Collections.singletonList(((ICPPASTUsingDirective) declaration).getQualifiedName()); + } else if (declaration instanceof ICPPASTUsingDeclaration) { + return Collections.singletonList(((ICPPASTUsingDeclaration) declaration).getName()); + } else if (declaration instanceof ICPPASTNamespaceAlias) { + return Collections.singletonList(((ICPPASTNamespaceAlias) declaration).getAlias()); + } else if (declaration instanceof ICPPASTAliasDeclaration) { + return Collections.singletonList(((ICPPASTAliasDeclaration) declaration).getAlias()); + } + return null; + } + + private static boolean isInsideTemplateDeclarationOrSpecialization(IASTNode node) { + while ((node = node.getParent()) != null) { + if (node instanceof ICPPASTTemplateDeclaration || node instanceof ICPPASTTemplateSpecialization) + return true; + } + + return false; + } + + private static void removeContainedNames(IASTNode node, Set names) { + NavigableSet containedNames = NameCollector.getContainedNames(node); + names.removeAll(containedNames); + } + + private static int skipWhitespaceBefore(int offset, String text) { + while (--offset >= 0) { + char c = text.charAt(offset); + if (!Character.isWhitespace(c)) + break; + } + return offset + 1; + } + + @Override + protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) + throws CoreException, OperationCanceledException { + // This method is no-op for this refactoring. The change is created in the createChange method. + } + + private class CandidateDeclarationFinder extends ASTVisitor { + final List declarations = new ArrayList<>(); + + CandidateDeclarationFinder() { + shouldVisitDeclarations = true; + shouldVisitNamespaces = true; + } + + @Override + public int visit(IASTDeclaration declaration) { + if (!declaration.isPartOfTranslationUnitFile()) + return PROCESS_SKIP; + if (declaration.getParent() instanceof IASTDeclarationListOwner) + declarations.add(declaration); + return PROCESS_CONTINUE; + } + + @Override + public int visit(ICPPASTNamespaceDefinition namespaceDefinition) { + if (!namespaceDefinition.isPartOfTranslationUnitFile()) + return PROCESS_SKIP; + declarations.add(namespaceDefinition); + return PROCESS_CONTINUE; + } + } + + /** + * Collects all simple names. + */ + private static class NameCollector extends ASTVisitor { + NavigableSet names = new SortedNodeSet<>(); + + static NavigableSet getContainedNames(IASTNode node) { + NameCollector collector = new NameCollector(); + node.accept(collector); + return collector.names; + } + + NameCollector() { + this.shouldVisitNames = true; + this.shouldVisitImplicitNames = true; + } + + @Override + public int visit(IASTName name) { + if (name instanceof ICPPASTQualifiedName || name instanceof ICPPASTTemplateId + || name instanceof ICPPASTConversionName) { + return PROCESS_CONTINUE; + } + names.add(name); + return PROCESS_CONTINUE; + } + } + + /** + * A set of AST nodes sorted by their offsets, or, if the offsets are equal, by the end offsets + * in the reverse order. + */ + private static class SortedNodeSet extends TreeSet { + private static final Comparator COMPARATOR = new Comparator() { + @Override + public int compare(IASTNode node1, IASTNode node2) { + int c = Integer.compare(ASTNodes.offset(node1), ASTNodes.offset(node2)); + if (c != 0) + return c; + return -Integer.compare(ASTNodes.endOffset(node1), ASTNodes.endOffset(node2)); + } + }; + + public SortedNodeSet() { + super(COMPARATOR); + } + } + + private static IASTDeclarationStatement getDeclarationStatement(IASTDeclaration declaration) { + while (true) { + IASTNode parent = declaration.getParent(); + if (parent instanceof IASTDeclarationStatement) + return (IASTDeclarationStatement) parent; + if (!(parent instanceof ICPPASTTemplateDeclaration)) + return null; + declaration = (IASTDeclaration) parent; + } + } + + private static 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; + } + + @Override + protected RefactoringDescriptor getRefactoringDescriptor() { + return null; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoringRunner.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoringRunner.java new file mode 100644 index 00000000000..81b1f02231e --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoringRunner.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.window.IShellProvider; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; + +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; +import org.eclipse.cdt.internal.ui.refactoring.RefactoringSaveHelper; + +public class RemoveUnusedDeclarationsRefactoringRunner extends RefactoringRunner { + + public RemoveUnusedDeclarationsRefactoringRunner(ICElement element, ISelection selection, + IShellProvider shellProvider, ICProject cProject) { + super(element, selection, shellProvider, cProject); + } + + @Override + public void run() { + RemoveUnusedDeclarationsRefactoring refactoring = + new RemoveUnusedDeclarationsRefactoring(element, selection, project); + RemoveUnusedDeclarationsWizard wizard = new RemoveUnusedDeclarationsWizard(refactoring); + run(wizard, refactoring, RefactoringSaveHelper.SAVE_NOTHING); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsWizard.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsWizard.java new file mode 100644 index 00000000000..56240521f30 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsWizard.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.reducer; + +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +import org.eclipse.cdt.ui.CUIPlugin; + +public class RemoveUnusedDeclarationsWizard extends RefactoringWizard { + public RemoveUnusedDeclarationsWizard(RemoveUnusedDeclarationsRefactoring refactoring) { + super(refactoring, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); + setDefaultPageTitle(Messages.RemoveFunctionBodiesRefactoring_RemoveFunctionBodies); + setDialogSettings(CUIPlugin.getDefault().getDialogSettings()); + } + + @Override + protected void addUserInputPages() { + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java index 0d5cd13e8b7..3b39defa7ae 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.utils; +import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; import org.eclipse.jface.viewers.ISelection; @@ -43,9 +44,9 @@ public class SelectionHelper { return null; } - public static IASTSimpleDeclaration findFirstSelectedDeclaration(final Region textSelection, + public static IASTSimpleDeclaration findFirstSelectedDeclaration(final IRegion textSelection, IASTTranslationUnit translationUnit) { - final Container container = new Container(); + final Container container = new Container<>(); translationUnit.accept(new ASTVisitor() { { @@ -64,18 +65,18 @@ public class SelectionHelper { return container.getObject(); } - public static boolean doesNodeOverlapWithRegion(IASTNode node, Region region) { + public static boolean doesNodeOverlapWithRegion(IASTNode node, IRegion region) { return doRegionsOverlap(getNodeSpan(node), region); } - public static boolean isNodeInsideRegion(IASTNode node, Region region) { + public static boolean isNodeInsideRegion(IASTNode node, IRegion region) { return isRegionInside(getNodeSpan(node), region); } /** * Returns true if the first region is inside the second. */ - private static boolean isRegionInside(Region region1, Region region2) { + private static boolean isRegionInside(IRegion region1, IRegion region2) { int offset1 = region1.getOffset(); int offset2 = region2.getOffset(); return offset1 >= offset2 && @@ -85,26 +86,26 @@ public class SelectionHelper { /** * Returns true if the two regions have at least one common point. */ - private static boolean doRegionsOverlap(Region region1, Region region2) { + private static boolean doRegionsOverlap(IRegion region1, IRegion region2) { int offset1 = region1.getOffset(); int offset2 = region2.getOffset(); return offset1 + region1.getLength() >= offset2 && offset1 <= offset2 + region2.getLength(); } - public static boolean isNodeInsideSelection(IASTNode node, Region selection) { + public static boolean isNodeInsideSelection(IASTNode node, IRegion selection) { return node.isPartOfTranslationUnitFile() && isNodeInsideRegion(node, selection); } - public static boolean isSelectionInsideNode(IASTNode node, Region selection) { + public static boolean isSelectionInsideNode(IASTNode node, IRegion selection) { return node.isPartOfTranslationUnitFile() && isRegionInside(selection, getNodeSpan(node)); } - public static boolean nodeMatchesSelection(IASTNode node, Region region) { + public static boolean nodeMatchesSelection(IASTNode node, IRegion region) { return getNodeSpan(node).equals(region); } - protected static Region getNodeSpan(IASTNode region) { + protected static IRegion getNodeSpan(IASTNode region) { int start = Integer.MAX_VALUE; int nodeLength = 0; IASTNodeLocation[] nodeLocations = region.getNodeLocations();