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();