From 7ac4d430102b07d0f2790018efa1cc0c30d6b0bc Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Thu, 14 Mar 2013 17:20:22 -0700 Subject: [PATCH] Bug 45203. Major milestone - Organize Includes command is ready for alpha testing. --- .../org.eclipse.cdt.core/META-INF/MANIFEST.MF | 2 +- .../cdt/core/parser/util/CharArrayUtils.java | 19 + .../core/dom/parser/ASTTranslationUnit.java | 48 + .../refactoring/RefactoringTestBase.java | 17 +- .../refactoring/RefactoringTestSuite.java | 2 + .../ExtractFunctionRefactoringTest.java | 15 +- .../includes/BindingClassifierTest.java | 88 +- .../includes/IncludesTestBase.java | 176 ++ .../includes/IncludesTestSuite.java} | 21 +- .../includes/OrganizeIncludesTest.java | 298 ++++ core/org.eclipse.cdt.ui/plugin.properties | 5 + core/org.eclipse.cdt.ui/plugin.xml | 22 + .../cdt/internal/ui/BusyCursorJobRunner.java | 145 ++ .../cdt/internal/ui/ICHelpContextIds.java | 7 +- .../ui/editor/CEditorActionContributor.java | 9 +- .../internal/ui/editor/CEditorMessages.java | 2 + .../ui/editor/CEditorMessages.properties | 2 + .../editor/ICEditorActionDefinitionIds.java | 6 + .../ui/editor/InteractiveHeaderChooser.java | 87 + .../ui/editor/OrganizeIncludesAction.java | 136 +- .../ui/preferences/IncludeStyleBlock.java | 27 +- .../ui/preferences/OrganizeIncludesBlock.java | 22 +- .../ui/preferences/PreferencesMessages.java | 1 + .../PreferencesMessages.properties | 1 + .../includes/BindingClassifier.java | 1415 +++++++++-------- .../includes/HeaderSubstitutor.java | 2 +- .../refactoring/includes/IHeaderChooser.java | 29 + .../ui/refactoring/includes/IncludeInfo.java | 2 +- .../ui/refactoring/includes/IncludeMap.java | 2 +- .../includes/IncludeOrganizer.java | 833 +++++++--- .../includes/IncludePreferences.java | 230 +-- .../ui/refactoring/includes/IncludeUtil.java | 2 +- .../includes/InclusionContext.java | 11 +- .../includes/InclusionRequest.java | 2 +- .../refactoring/includes/Messages.properties | 11 - .../eclipse/cdt/ui/PreferenceConstants.java | 191 +++ .../cdt/ui/actions/CdtActionConstants.java | 7 + .../cdt/ui/actions/GenerateActionGroup.java | 2 +- 38 files changed, 2665 insertions(+), 1232 deletions(-) create mode 100644 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java rename core/{org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java => org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java} (54%) create mode 100644 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IHeaderChooser.java delete mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index 9130967645b..4055b126fa7 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -59,7 +59,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.dom.rewrite;x-friends:="org.eclipse.cdt.core.tests,org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.astwriter;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;x-internal:=true, - org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-internal:=true, + org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.util;x-internal:=true, org.eclipse.cdt.internal.core.envvar;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.managedbuilder.core", org.eclipse.cdt.internal.core.index;x-friends:="org.eclipse.cdt.ui", diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java index 330878bb36c..6307942c555 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java @@ -74,6 +74,21 @@ public class CharArrayUtils { return true; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of a string. + * @since 5.5 + */ + public static final boolean equals(char[] str1, int start1, int length1, String str2) { + if (length1 != str2.length() || str1.length < length1 + start1) + return false; + for (int i = 0; i < length1; ++i) { + if (str1[start1++] != str2.charAt(i)) + return false; + } + return true; + } + /** * Returns {@code true} if a prefix of the character array is the same as contents * of a string. @@ -117,6 +132,10 @@ public class CharArrayUtils { return str1.length - str2.length; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of another character array. + */ public static final boolean equals(char[] str1, int start1, int length1, char[] str2) { if (length1 != str2.length || str1.length < length1 + start1) return false; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java index 17e83bb272a..b6b9281b02c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java @@ -485,4 +485,52 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat } return fSizeofCalculator; } + + /** + * Returns the offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() : -1; + } + + /** + * Returns the end offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeEndOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() : -1; + } + + /** + * Returns the 1-based starting line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getStartingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getStartingLineNumber() : 0; + } + + /** + * Returns the 1-based ending line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getEndingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getEndingLineNumber() : 0; + } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java index e1836964b4f..70109e01c1b 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 @@ -22,7 +22,6 @@ import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.TextSelection; @@ -56,8 +55,6 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; * Common base for refactoring tests. */ public abstract class RefactoringTestBase extends BaseTestCase { - protected static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); - /** Allows empty files to be created during test setup. */ protected boolean createEmptyFiles = true; /** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */ @@ -137,7 +134,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { public void tearDown() throws Exception { if (cproject != null) { cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, - NULL_PROGRESS_MONITOR); + npm()); } resetPreferences(); super.tearDown(); @@ -175,7 +172,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { protected void executeRefactoring(Refactoring refactoring, RefactoringContext context, boolean withUserInput, boolean expectedSuccess) throws CoreException, Exception { try { - RefactoringStatus initialStatus = refactoring.checkInitialConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus initialStatus = refactoring.checkInitialConditions(npm()); if (!expectedSuccess) { assertStatusFatalError(initialStatus); return; @@ -190,7 +187,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { if (withUserInput) simulateUserInput(); - RefactoringStatus finalStatus = refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus finalStatus = refactoring.checkFinalConditions(npm()); if (expectedFinalWarnings != 0) { assertStatusWarning(finalStatus, expectedFinalWarnings); } else if (expectedFinalInfos != 0) { @@ -198,8 +195,8 @@ public abstract class RefactoringTestBase extends BaseTestCase { } else { assertStatusOk(finalStatus); } - Change change = refactoring.createChange(NULL_PROGRESS_MONITOR); - change.perform(NULL_PROGRESS_MONITOR); + Change change = refactoring.createChange(npm()); + change.perform(npm()); } finally { if (context != null) context.dispose(); @@ -212,7 +209,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { RefactoringHistory history = RefactoringHistoryService.getInstance().readRefactoringHistory( new ByteArrayInputStream(scriptSource.getBytes()), 0); for (RefactoringDescriptorProxy proxy : history.getDescriptors()) { - RefactoringDescriptor descriptor = proxy.requestDescriptor(NULL_PROGRESS_MONITOR); + RefactoringDescriptor descriptor = proxy.requestDescriptor(npm()); RefactoringStatus status = new RefactoringStatus(); RefactoringContext context = descriptor.createRefactoringContext(status); assertTrue(status.isOK()); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index 3969debfd10..496e5c0bca4 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVar import org.eclipse.cdt.ui.tests.refactoring.gettersandsetters.GenerateGettersAndSettersTest; import org.eclipse.cdt.ui.tests.refactoring.hidemethod.HideMethodRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest; +import org.eclipse.cdt.ui.tests.refactoring.includes.IncludesTestSuite; import org.eclipse.cdt.ui.tests.refactoring.rename.RenameRegressionTests; import org.eclipse.cdt.ui.tests.refactoring.togglefunction.ToggleRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.utils.UtilTestSuite; @@ -42,6 +43,7 @@ public class RefactoringTestSuite extends TestSuite { suite.addTest(ImplementMethodRefactoringTest.suite()); suite.addTest(ExtractLocalVariableRefactoringTest.suite()); suite.addTest(ToggleRefactoringTest.suite()); + suite.addTest(IncludesTestSuite.suite()); return suite; } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java index 4446a6de752..76a7edcea93 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java @@ -1,11 +1,11 @@ /******************************************************************************* * Rapperswil, University of applied sciences 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: + * 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: * Institute for Software - initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ @@ -181,7 +181,6 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { assertRefactoringSuccess(); } - //A.h //#ifndef A_H_ //#define A_H_ @@ -308,7 +307,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { public void testLocalVariableDeclaration_3() throws Exception { assertRefactoringSuccess(); } - + //A.h //#ifndef A_H_ //#define A_H_ diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java index 6a2f0f19728..c9329d20606 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 @@ -10,12 +10,16 @@ *******************************************************************************/ package org.eclipse.cdt.ui.tests.refactoring.includes; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.TreeSet; import junit.framework.TestSuite; +import com.ibm.icu.text.MessageFormat; + import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; @@ -51,7 +55,8 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { fIndex.acquireReadLock(); ITranslationUnit tu = ast.getOriginatingTranslationUnit(); fContext = new InclusionContext(tu, fIndex); - fBindingClassifier = new BindingClassifier(fContext, ast); + fBindingClassifier = new BindingClassifier(fContext); + fBindingClassifier.classifyNodeContents(ast); } @Override @@ -69,15 +74,36 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { } private void assertExpectedBindings(String[] expectedNames, Set bindings, String verb) { - Set remaining = new HashSet(Arrays.asList(expectedNames)); + Set expected = new TreeSet(Arrays.asList(expectedNames)); + Set extra = new TreeSet(); for (IBinding binding : bindings) { - String name = binding.getName(); - if (!remaining.remove(name)) { - fail("Binding \"" + name + "\" should not be " + verb); - } + extra.add(binding.getName()); } - if (!remaining.isEmpty()) - fail("Binding \"" + remaining.iterator().next() + "\" is not " + verb); + Set missing = new TreeSet(expected); + missing.removeAll(extra); + extra.removeAll(expected); + if (extra.isEmpty() && missing.isEmpty()) + return; + List errors = new ArrayList(2); + if (!missing.isEmpty()) { + errors.add(MessageFormat.format("{0,choice,1#Binding|1 strings, String delimiter) { + StringBuilder buf = new StringBuilder(); + for (String str : strings) { + if (buf.length() != 0) + buf.append(delimiter); + buf.append(str); + } + return buf.toString(); } // class A; @@ -109,4 +135,46 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { public void testClassMember() throws Exception { assertDefined("f", "A"); } + + // class A { void m(); }; + + // void test(A* a) { + // a->m(); + // } + public void testMethodCall() throws Exception { + assertDefined("A"); + } + + // struct A {}; + // struct B {}; + + // struct C { + // A a; + // static B b; + // }; + public void testFieldReference() throws Exception { + assertDefined("A"); + assertDeclared("B"); + } + + // int a; + + // void test() { + // void* x = &a; + // } + public void testVariableReference() throws Exception { + assertDefined("a"); // Forward declaration of variables is not allowed by default. + } + + // struct A { + // void operator()(int p); + // }; + // const A a; + + // void test() { + // a(1); + // } + public void testCallOperator() throws Exception { + assertDefined("A", "a"); // Forward declaration of variables is not allowed by default. + } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java new file mode 100644 index 00000000000..e4594327976 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2013 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.refactoring.includes; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.preference.IPreferenceStore; +import org.osgi.framework.Bundle; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; +import org.eclipse.cdt.ui.tests.refactoring.TestSourceFile; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Common base for include-related tests. + */ +public abstract class IncludesTestBase extends BaseTestCase { + protected final String LINE_DELIMITER = "\n"; + + protected static class FirstHeaderChooser implements IHeaderChooser { + @Override + public IPath chooseHeader(String bindingName, Collection headers) { + return headers.isEmpty() ? null : headers.iterator().next(); + } + } + + /** Expected counts of errors, warnings and info messages */ + protected int expectedInitialErrors; + protected int expectedInitialWarnings; + protected int expectedFinalWarnings; + protected int expectedFinalInfos; + + protected IIndex index; + protected ICProject cproject; + protected IASTTranslationUnit ast; + protected TestSourceFile selectedFile; + private StringBuilder[] testData; + private boolean cpp = true; + private final Set testFiles = new LinkedHashSet(); + + protected IncludesTestBase() { + super(); + } + + protected IncludesTestBase(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + resetPreferences(); + cproject = cpp ? + CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) : + CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER); + Bundle bundle = CTestPlugin.getDefault().getBundle(); + CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0); + + IFile sourceFile = null; + for (CharSequence contents : testData) { + TestSourceFile testFile = null; + boolean expectedResult = false; + BufferedReader reader = new BufferedReader(new StringReader(contents.toString())); + String line; + while ((line = reader.readLine()) != null) { + String trimmedLine = line.trim(); + if (testFile == null) { + assertTrue("Invalid file name \"" + trimmedLine + "\"", trimmedLine.matches("^(\\w+/)*\\w+\\.\\w+$")); + testFile = new TestSourceFile(trimmedLine); + } else if (isResultDelimiter(trimmedLine)) { + expectedResult = true; + } else if (expectedResult) { + testFile.addLineToExpectedSource(line); + } else { + testFile.addLineToSource(line); + } + } + reader.close(); + + sourceFile = TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()), + testFile.getSource()); + testFiles.add(testFile); + selectedFile = testFile; + } + CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER); + waitForIndexer(cproject); + + index= CCorePlugin.getIndexManager().getIndex(cproject); + + index.acquireReadLock(); + ast = TestSourceReader.createIndexBasedAST(index, cproject, sourceFile); + } + + @Override + public void tearDown() throws Exception { + if (cproject != null) { + cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, npm()); + } + resetPreferences(); + super.tearDown(); + } + + protected ICProject getCProject() { + return cproject; + } + + protected TestSourceFile getSelectedTestFile() { + return selectedFile; + } + + protected IFile getSelectedFile() { + if (selectedFile == null) + return null; + return cproject.getProject().getFile(new Path(selectedFile.getName())); + } + + protected ITranslationUnit getSelectedTranslationUnit() { + IFile file = getSelectedFile(); + if (file == null) + return null; + return (ITranslationUnit) CoreModel.getDefault().create(file); + } + + protected boolean isCpp() { + return cpp; + } + + protected void setCpp(boolean cpp) { + this.cpp = cpp; + } + + private boolean isResultDelimiter(String str) { + if (str.isEmpty()) + return false; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) != '=') + return false; + } + return true; + } + + protected void resetPreferences() { + } + + protected IPreferenceStore getPreferenceStore() { + return CUIPlugin.getDefault().getPreferenceStore(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java similarity index 54% rename from core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java rename to core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java index 310e815cf71..cb49b4da418 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2013 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 @@ -8,18 +8,17 @@ * Contributors: * Sergey Prigogin (Google) - initial API and implementation *******************************************************************************/ -package org.eclipse.cdt.internal.ui.refactoring.includes; +package org.eclipse.cdt.ui.tests.refactoring.includes; -import org.eclipse.osgi.util.NLS; +import junit.framework.Test; +import junit.framework.TestSuite; -final class Messages extends NLS { - public static String IncludeOrganizer_ChooseHeader; +public class IncludesTestSuite extends TestSuite { - static { - NLS.initializeMessages(Messages.class.getName(), Messages.class); - } - - // Do not instantiate - private Messages() { + public static Test suite() throws Exception { + IncludesTestSuite suite = new IncludesTestSuite(); + suite.addTest(BindingClassifierTest.suite()); + suite.addTest(OrganizeIncludesTest.suite()); + return suite; } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java new file mode 100644 index 00000000000..50fa7d63d68 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2013 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.refactoring.includes; + +import java.util.List; + +import junit.framework.Test; + +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; + +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; + +/** + * Tests for Extract Function refactoring. + */ +public class OrganizeIncludesTest extends IncludesTestBase { + + public OrganizeIncludesTest() { + super(); + } + + public OrganizeIncludesTest(String name) { + super(name); + } + + public static Test suite() { + return suite(OrganizeIncludesTest.class); + } + + @Override + protected void resetPreferences() { + super.resetPreferences(); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_ENUMS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_TEMPLATES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_ALLOW_REORDERING); + } + + private void assertExpectedResults() throws Exception { + String actual = organizeIncludes(ast.getOriginatingTranslationUnit()); + assertEquals(selectedFile.getExpectedSource(), actual); + } + + /** + * Invokes include organizer and returns the new contents of the translation unit. + */ + private String organizeIncludes(ITranslationUnit tu) throws Exception { + IHeaderChooser headerChooser = new FirstHeaderChooser(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser); + List edits = organizer.organizeIncludes(ast); + IDocument document = new Document(new String(tu.getContents())); + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + edit.apply(document); + } + return document.get(); + } + + //h1.h + //typedef int my_type; + + //A.h + //class A { + // my_type m1(); + //}; + + //A.cpp + //// Comment line 1 + //// Comment line 2 + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + //==================== + //// Comment line 1 + //// Comment line 2 + // + //#include "A.h" + // + //#include "h1.h" + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + public void testNoExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //B.h + //class B {}; + + //C.h + //class C {}; + + //A.h + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + //==================== + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //#include "B.h" + // + //class C; + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + public void testIncludeGuards() throws Exception { + assertExpectedResults(); + } + + //B.h + //template class B {}; + + //C.h + //class C {}; + + //A.h + //#pragma once + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + //==================== + //#pragma once + // + //#include "B.h" + //#include "C.h" + // + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + public void testPragmaOnce() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "A.h" + // + ////#include "h1.h" // Unused + //#include "h2.h" /* Required */ // another comment + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum class Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "h2.h" /* Required */ // another comment + ////#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#include "A.h" + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludesNoReordering() throws Exception { + getPreferenceStore().setValue(PreferenceConstants.INCLUDES_ALLOW_REORDERING, false); + assertExpectedResults(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 3ad091cf1eb..5cdafd68387 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -276,6 +276,10 @@ ActionDefinition.openType.description= Open an element in an Editor ActionDefinition.addInclude.name= Add Include ActionDefinition.addInclude.description= Create include statement on selection +#Organize Includes +ActionDefinition.addInclude.name= Organize Includes +ActionDefinition.addInclude.description= Evaluates all required includes and replaces the current includes + #Sort Lines ActionDefinition.sortLines.name= Sort Lines ActionDefinition.sortLines.description= Sort selected lines alphabetically @@ -593,6 +597,7 @@ renameParticipant.name = Source Folder Rename FormatAction.label= &Format IndentAction.label= Correct &Indentation +OrganizeIncludesAction.label= Or&ganize Includes AddIncludeAction.label= A&dd Include SortLinesAction.label= Sor&t Lines CommentAction.label= Co&mment diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index f2135dbd9a1..901b156a59f 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1884,6 +1884,13 @@ menubarPath="org.eclipse.jdt.ui.source.menu/importGroup" id="org.eclipse.cdt.ui.actions.SortLines"> + + + + + + @@ -4628,6 +4647,9 @@ + + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java new file mode 100644 index 00000000000..5c4545bca92 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2013 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.internal.ui; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressService; + +import org.eclipse.cdt.ui.CUIPlugin; + +/** + * Synchronously executes a {@link Job} while allowing user to cancel it if it takes too long. + */ +public final class BusyCursorJobRunner { + /** + * Adapts a {@link Job} to be an {@link IRunnableWithProgress}. + */ + private static class JobRunnableWithProgressAdapter implements IRunnableWithProgress { + private final Job job; + + /** + * Creates the {@link IRunnableWithProgress} from the {@link Job}. + */ + public JobRunnableWithProgressAdapter(Job job) { + this.job = job; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + IStatus result; + try { + monitor.beginTask(job.getName(), IProgressMonitor.UNKNOWN); + result = executeAndWait(job, monitor); + } catch (RuntimeException e) { + throw new InvocationTargetException(e); + } + + switch (result.getSeverity()) { + case IStatus.CANCEL: + throw new InterruptedException(); + + case IStatus.ERROR: + if (result.getException() instanceof OperationCanceledException) { + throw new InterruptedException(); + } + throw new InvocationTargetException(new CoreException(result)); + } + } + } + + /** + * Runs the given job and waits for it to finish. If executing in the UI thread, sets the cursor + * to busy while the job is being executed. + * + * @param job the job to execute + * @return the status reflecting the result of the job execution + */ + public static IStatus execute(Job job) { + boolean inUiThread = Thread.currentThread() == Display.getDefault().getThread(); + if (inUiThread) { + return busyCursorWhile(job); + } + return executeAndWait(job, new NullProgressMonitor()); + } + + private static IStatus busyCursorWhile(Job job) { + try { + IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); + progressService.busyCursorWhile(new JobRunnableWithProgressAdapter(job)); + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; // Operation was cancelled. + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + if (targetException instanceof CoreException) { + return ((CoreException) targetException).getStatus(); + } + return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, e.getMessage(), e); + } + return Status.OK_STATUS; + } + + private static IStatus executeAndWait(final Job job, IProgressMonitor monitor) { + final IStatus[] statusHolder = new IStatus[1]; + + IJobManager jobManager = Job.getJobManager(); + JobChangeAdapter listener = new JobChangeAdapter() { + @Override + public void done(IJobChangeEvent event) { + super.done(event); + if (event.getJob() == job) { + synchronized (statusHolder) { + statusHolder[0] = event.getResult(); + statusHolder.notifyAll(); + } + } + } + }; + jobManager.addJobChangeListener(listener); + job.schedule(); + + try { + synchronized (statusHolder) { + while (statusHolder[0] == null) { + try { + statusHolder.wait(100); + if (monitor.isCanceled()) { + job.cancel(); + } + } catch (InterruptedException e) { + job.cancel(); + return Status.CANCEL_STATUS; + } + } + + return statusHolder[0]; + } + } finally { + monitor.done(); + jobManager.removeJobChangeListener(listener); + } + } + + private BusyCursorJobRunner() {} +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java index 44a508859bf..a142be73da1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 IBM Corporation and others. + * Copyright (c) 2006, 2013 IBM Corporation 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 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * QNX Software System * Anton Leherbauer (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui; @@ -19,7 +20,6 @@ import org.eclipse.cdt.ui.CUIPlugin; *

* This interface contains constants only; it is not intended to be implemented or extended. *

- * */ public interface ICHelpContextIds { public static final String PREFIX = CUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$ @@ -33,9 +33,10 @@ public interface ICHelpContextIds { public static final String OPEN_PROJECT_WIZARD_ACTION = PREFIX + "open_project_wizard_action"; //$NON-NLS-1$ public static final String CONVERT_TO_CCPP_WIZARD_PAGE = PREFIX + "cdt_t_conv_proj_context"; //$NON-NLS-1$ public static final String NEW_C_FILE_WIZARD_PAGE = PREFIX + "cdt_creating_cpp_file_context"; //$NON-NLS-1$ - // Actions + // Actions public static final String ADD_INCLUDE_ON_SELECTION_ACTION = PREFIX + "add_includes_on_selection_action_context"; //$NON-NLS-1$; + public static final String ORGANIZE_INCLUDES_ACTION = PREFIX + "organize_includes_action"; //$NON-NLS-1$; public static final String FILTER_PUBLIC_ACTION= PREFIX + "filter_public_action"; //$NON-NLS-1$ public static final String FILTER_FIELDS_ACTION= PREFIX + "filter_fields_action"; //$NON-NLS-1$ public static final String FILTER_STATIC_ACTION= PREFIX + "filter_static_action"; //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java index 1338d9d257a..4894e4aa2c8 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java @@ -33,14 +33,14 @@ import org.eclipse.ui.texteditor.RetargetTextEditorAction; import org.eclipse.cdt.ui.actions.CdtActionConstants; import org.eclipse.cdt.internal.ui.IContextMenuConstants; +import org.eclipse.cdt.internal.ui.actions.FindWordAction; +import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; +import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; +import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectHistoryAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectNextAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectPreviousAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectionAction; -import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; -import org.eclipse.cdt.internal.ui.actions.FindWordAction; -import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; -import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; public class CEditorActionContributor extends TextEditorActionContributor { @@ -229,6 +229,7 @@ public class CEditorActionContributor extends TextEditorActionContributor { bars.setGlobalActionHandler(CdtActionConstants.REMOVE_BLOCK_COMMENT, getAction(textEditor, "RemoveBlockComment")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.INDENT, getAction(textEditor, "Indent")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, getAction(textEditor, "AddIncludeOnSelection")); //$NON-NLS-1$ + bars.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, getAction(textEditor, "OrganizeIncludes")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.SORT_LINES, getAction(textEditor, "SortLines")); //$NON-NLS-1$ IAction action= getAction(textEditor, ITextEditorActionConstants.REFRESH); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java index 0866f7cc9e9..5d0e53a8d03 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java @@ -38,10 +38,12 @@ public final class CEditorMessages extends NLS { public static String AddIncludesOperation_description; public static String OrganizeIncludes_label; public static String OrganizeIncludes_description; + public static String OrganizeIncludes_action; public static String OrganizeIncludes_error_title; public static String OrganizeIncludes_insertion_failed; public static String OrganizeIncludes_help_provider_error; public static String OrganizeIncludes_failed; + public static String OrganizeIncludes_choose_header; public static String OrganizeIncludesOperation_description; public static String ShowInCView_description; public static String ShowInCView_label; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index bf34fb659c8..62aa9c4416e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -24,10 +24,12 @@ AddIncludesOperation_description=Adding include statement OrganizeIncludes_label=Organize Includes OrganizeIncludes_description=Organize includes for current file +OrganizeIncludes_action=Organizing includes OrganizeIncludes_error_title=Error Organizing Includes OrganizeIncludes_insertion_failed=Adding include statements failed OrganizeIncludes_help_provider_error=Help provider error OrganizeIncludes_failed=Organize Includes operation failed +OrganizeIncludes_choose_header=Choose a header file to include for symbol ''{0}'' OrganizeIncludesOperation_description=Organizing include statements ShowInCView_description=Show the current resource in the C/C++ Projects view diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java index 33fd516ea1f..9808ef10801 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java @@ -72,6 +72,12 @@ public interface ICEditorActionDefinitionIds extends ITextEditorActionDefinition */ public static final String ADD_INCLUDE= "org.eclipse.cdt.ui.edit.text.c.add.include"; //$NON-NLS-1$ + /** + * Action definition ID of the source -> organize includes action + * (value "org.eclipse.cdt.ui.edit.text.c.organize.includes"). + */ + public static final String ORGANIZE_INCLUDES= "org.eclipse.cdt.ui.edit.text.c.organize.includes"; //$NON-NLS-1$ + /** * Action definition ID of the open declaration action * (value "org.eclipse.cdt.ui.edit.opendecl"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java new file mode 100644 index 00000000000..4adcddebc56 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2013 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.internal.ui.editor; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Dialog-based header chooser. + */ +public class InteractiveHeaderChooser implements IHeaderChooser { + private final Shell shell; + Map, IPath> userChoiceCache; + + public InteractiveHeaderChooser(Shell shell) { + this.shell = shell; + userChoiceCache = new HashMap, IPath>(); + } + + @Override + public IPath chooseHeader(final String bindingName, Collection headers) { + if (headers.isEmpty()) + return null; + if (headers.size() == 1) + return headers.iterator().next(); + + Set cacheKey = new HashSet(headers); + // Check the decision cache. If the cache doesn't help, ask the user. + // Query the cache. + if (userChoiceCache.containsKey(cacheKey)) { + return userChoiceCache.get(cacheKey); + } + + // Ask the user. + final IPath[] elemArray = headers.toArray(new IPath[headers.size()]); + final IPath[] selectedElement = new IPath[1]; + runInUIThread(new Runnable() { + @Override + public void run() { + if (!shell.isDisposed()) { + ElementListSelectionDialog dialog = + new ElementListSelectionDialog(shell, new LabelProvider()); + dialog.setElements(elemArray); + dialog.setTitle(CEditorMessages.OrganizeIncludes_label); + dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName)); + if (dialog.open() == Window.OK) { + selectedElement[0] = (IPath) dialog.getFirstResult(); + } + } + } + }); + + IPath selectedHeader = selectedElement[0]; + if (selectedHeader != null) + userChoiceCache.put(headers, selectedHeader); // Remember user's choice. + return selectedHeader; + } + + private void runInUIThread(Runnable runnable) { + if (Display.getCurrent() != null) { + runnable.run(); + } else { + Display.getDefault().syncExec(runnable); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java index 8ca3aae0858..72dcc7d6c5e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java @@ -7,12 +7,25 @@ * * Contributors: * Mathias Kunter - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.undo.DocumentUndoManagerRegistry; +import org.eclipse.text.undo.IDocumentUndoManager; +import org.eclipse.ui.IEditorInput; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.TextEditorAction; @@ -25,7 +38,9 @@ import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.ui.text.SharedASTJob; +import org.eclipse.cdt.internal.ui.BusyCursorJobRunner; import org.eclipse.cdt.internal.ui.ICHelpContextIds; +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; /** @@ -37,9 +52,84 @@ public class OrganizeIncludesAction extends TextEditorAction { * @param editor The editor on which this organize includes action should operate. */ public OrganizeIncludesAction(ITextEditor editor) { - // TODO Fix ID's super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$ - CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION); + CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ORGANIZE_INCLUDES_ACTION); + } + + @Override + public void run() { + final ITextEditor editor = getTextEditor(); + final ITranslationUnit tu = getTranslationUnit(editor); + if (tu == null) { + return; + } + if (!validateEditorInputState()) { + return; + } + + final IHeaderChooser headerChooser = new InteractiveHeaderChooser(editor.getSite().getShell()); + final String lineDelimiter = getLineDelimiter(editor); + final List edits = new ArrayList(); + SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) { + @Override + public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { + IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(), + IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); + try { + index.acquireReadLock(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser); + edits.addAll(organizer.organizeIncludes(ast)); + return Status.OK_STATUS; + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } finally { + index.releaseReadLock(); + } + } + }; + IStatus status = BusyCursorJobRunner.execute(job); + if (status.isOK()) { + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document); + manager.beginCompoundChange(); + try { + edit.apply(document); + } catch (MalformedTreeException e) { + CUIPlugin.log(e); + } catch (BadLocationException e) { + CUIPlugin.log(e); + } + manager.endCompoundChange(); + } + } else if (status.matches(IStatus.ERROR)) { + ErrorDialog.openError(editor.getEditorSite().getShell(), + CEditorMessages.OrganizeIncludes_error_title, + CEditorMessages.OrganizeIncludes_insertion_failed, status); + } + } + + private static String getLineDelimiter(ITextEditor editor) { + try { + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + String delim= document.getLineDelimiter(0); + if (delim != null) { + return delim; + } + } catch (BadLocationException e) { + } + return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public void update() { + ITextEditor editor = getTextEditor(); + setEnabled(editor != null && getTranslationUnit(editor) != null); } /** @@ -53,46 +143,4 @@ public class OrganizeIncludesAction extends TextEditorAction { } return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); } - - @Override - public void run() { - final ITextEditor editor = getTextEditor(); - final ITranslationUnit tu = getTranslationUnit(editor); - if (tu == null) { - return; - } - try { - if (!validateEditorInputState()) { - return; - } - - SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_label, tu) { - @Override - public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { - IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(), - IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); - try { - index.acquireReadLock(); - IncludeOrganizer organizer = new IncludeOrganizer(editor, tu, index); - organizer.organizeIncludes(ast); - return Status.OK_STATUS; - } catch (InterruptedException e) { - return Status.CANCEL_STATUS; - } finally { - index.releaseReadLock(); - } - } - }; - job.schedule(); - job.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - @Override - public void update() { - ITextEditor editor = getTextEditor(); - setEnabled(editor != null && getTranslationUnit(editor) != null); - } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java index acae20bcb7b..e47842b5ddd 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeStyleBlock.java @@ -19,27 +19,28 @@ import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; +import org.eclipse.cdt.ui.PreferenceConstants; + import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind; -import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences; /** * The preference block for configuring style of include statements. */ public class IncludeStyleBlock extends TabConfigurationBlock { - static final Key KEY_STYLE_RELATED = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_RELATED); - static final Key KEY_STYLE_PARTNER = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_PARTNER); - static final Key KEY_STYLE_SAME_FOLDER = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SAME_FOLDER); - static final Key KEY_STYLE_SUBFOLDER = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SUBFOLDER); - static final Key KEY_STYLE_SYSTEM = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SYSTEM); - static final Key KEY_STYLE_SYSTEM_WITH_EXTENSION = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SYSTEM_WITH_EXTENSION); - static final Key KEY_STYLE_SYSTEM_WITHOUT_EXTENSION = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION); - static final Key KEY_STYLE_OTHER = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_OTHER); - static final Key KEY_STYLE_SAME_PROJECT = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_SAME_PROJECT); - static final Key KEY_STYLE_OTHER_PROJECT = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_OTHER_PROJECT); - static final Key KEY_STYLE_EXTERNAL = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_EXTERNAL); - static final Key KEY_STYLE_MATCHING_PATTERN = getCDTUIKey(IncludePreferences.PREF_INCLUDE_STYLE_MATCHING_PATTERN); + static final Key KEY_STYLE_RELATED = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_RELATED); + static final Key KEY_STYLE_PARTNER = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_PARTNER); + static final Key KEY_STYLE_SAME_FOLDER = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SAME_FOLDER); + static final Key KEY_STYLE_SUBFOLDER = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SUBFOLDER); + static final Key KEY_STYLE_SYSTEM = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SYSTEM); + static final Key KEY_STYLE_SYSTEM_WITH_EXTENSION = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITH_EXTENSION); + static final Key KEY_STYLE_SYSTEM_WITHOUT_EXTENSION = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION); + static final Key KEY_STYLE_OTHER = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_OTHER); + static final Key KEY_STYLE_SAME_PROJECT = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_SAME_PROJECT); + static final Key KEY_STYLE_OTHER_PROJECT = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_OTHER_PROJECT); + static final Key KEY_STYLE_EXTERNAL = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_EXTERNAL); + static final Key KEY_STYLE_MATCHING_PATTERN = getCDTUIKey(PreferenceConstants.INCLUDE_STYLE_MATCHING_PATTERN); static final Map KEY_MAP = createKeyMap(); static final Key[] STYLE_KEYS = KEY_MAP.values().toArray(new Key[KEY_MAP.size()]); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OrganizeIncludesBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OrganizeIncludesBlock.java index c82bfe53231..ddf21e116a9 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OrganizeIncludesBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OrganizeIncludesBlock.java @@ -17,9 +17,10 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; +import org.eclipse.cdt.ui.PreferenceConstants; + import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; -import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences.UnusedStatementDisposition; import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; @@ -27,13 +28,14 @@ import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; * The preference block for configuring Organize Includes command. */ public class OrganizeIncludesBlock extends OptionsConfigurationBlock { - private static final Key KEY_HEURISTIC_HEADER_SUBSTITUTION = getCDTUIKey(IncludePreferences.PREF_HEURISTIC_HEADER_SUBSTITUTION); - private static final Key KEY_INCLUDES_REORDERING = getCDTUIKey(IncludePreferences.PREF_INCLUDES_REORDERING); - private static final Key KEY_UNUSED_STATEMENTS_DISPOSITION = getCDTUIKey(IncludePreferences.PREF_UNUSED_STATEMENTS_DISPOSITION); - private static final Key KEY_FORWARD_DECLARE_COMPOSITE_TYPES = getCDTUIKey(IncludePreferences.PREF_FORWARD_DECLARE_COMPOSITE_TYPES); - private static final Key KEY_FORWARD_DECLARE_ENUMS = getCDTUIKey(IncludePreferences.PREF_FORWARD_DECLARE_ENUMS); - private static final Key KEY_FORWARD_DECLARE_FUNCTIONS = getCDTUIKey(IncludePreferences.PREF_FORWARD_DECLARE_FUNCTIONS); - private static final Key KEY_FORWARD_DECLARE_NAMESPACE_ELEMENTS = getCDTUIKey(IncludePreferences.PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS); + private static final Key KEY_HEURISTIC_HEADER_SUBSTITUTION = getCDTUIKey(PreferenceConstants.INCLUDES_HEURISTIC_HEADER_SUBSTITUTION); + private static final Key KEY_INCLUDES_REORDERING = getCDTUIKey(PreferenceConstants.INCLUDES_ALLOW_REORDERING); + private static final Key KEY_UNUSED_STATEMENTS_DISPOSITION = getCDTUIKey(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION); + private static final Key KEY_FORWARD_DECLARE_COMPOSITE_TYPES = getCDTUIKey(PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES); + private static final Key KEY_FORWARD_DECLARE_ENUMS = getCDTUIKey(PreferenceConstants.FORWARD_DECLARE_ENUMS); + private static final Key KEY_FORWARD_DECLARE_FUNCTIONS = getCDTUIKey(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS); + private static final Key KEY_FORWARD_DECLARE_TEMPLATES = getCDTUIKey(PreferenceConstants.FORWARD_DECLARE_TEMPLATES); + private static final Key KEY_FORWARD_DECLARE_NAMESPACE_ELEMENTS = getCDTUIKey(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS); private static final String[] DISPOSITION_VALUES = { UnusedStatementDisposition.REMOVE.toString(), @@ -53,6 +55,7 @@ public class OrganizeIncludesBlock extends OptionsConfigurationBlock { KEY_FORWARD_DECLARE_COMPOSITE_TYPES, KEY_FORWARD_DECLARE_ENUMS, KEY_FORWARD_DECLARE_FUNCTIONS, + KEY_FORWARD_DECLARE_TEMPLATES, KEY_FORWARD_DECLARE_NAMESPACE_ELEMENTS, }; @@ -87,6 +90,9 @@ public class OrganizeIncludesBlock extends OptionsConfigurationBlock { control = addCheckBox(composite, PreferencesMessages.OrganizeIncludesBlock_forward_declare_functions, KEY_FORWARD_DECLARE_FUNCTIONS, TRUE_FALSE, 0); LayoutUtil.setHorizontalSpan(control, 2); + control = addCheckBox(composite, PreferencesMessages.OrganizeIncludesBlock_forward_declare_templates, + KEY_FORWARD_DECLARE_TEMPLATES, TRUE_FALSE, 0); + LayoutUtil.setHorizontalSpan(control, 2); control = addCheckBox(composite, PreferencesMessages.OrganizeIncludesBlock_forward_declare_namespace_elements, KEY_FORWARD_DECLARE_NAMESPACE_ELEMENTS, TRUE_FALSE, 0); LayoutUtil.setHorizontalSpan(control, 2); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java index a3d4c6e2cd8..a3f32d56244 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java @@ -443,6 +443,7 @@ public final class PreferencesMessages extends NLS { public static String OrganizeIncludesBlock_forward_declare_composite_types; public static String OrganizeIncludesBlock_forward_declare_enums; public static String OrganizeIncludesBlock_forward_declare_functions; + public static String OrganizeIncludesBlock_forward_declare_templates; public static String OrganizeIncludesBlock_forward_declare_namespace_elements; public static String OrganizeIncludesBlock_heuristic_header_substitution; public static String OrganizeIncludesBlock_unused_statements; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties index 360fae0dbba..bbffa133536 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties @@ -499,6 +499,7 @@ OrganizeIncludesBlock_allow_reordering= Allow &reordering of includes OrganizeIncludesBlock_forward_declare_composite_types= Forward declare &classes, structs and unions OrganizeIncludesBlock_forward_declare_enums= Forward declare &enums when possible OrganizeIncludesBlock_forward_declare_functions= Forward declare &functions +OrganizeIncludesBlock_forward_declare_templates= Forward declare &templates OrganizeIncludesBlock_forward_declare_namespace_elements= Forward declare &namespace elements OrganizeIncludesBlock_heuristic_header_substitution= Allow &heuristic header file substitution OrganizeIncludesBlock_unused_statements= &Unused Includes and Forward Declarations: diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java index 81d76f66f4a..fd1854e7d88 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 @@ -24,6 +24,8 @@ import java.util.Set; import org.eclipse.core.runtime.CoreException; +import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; @@ -42,6 +44,8 @@ import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; import org.eclipse.cdt.core.dom.ast.IASTIfStatement; +import org.eclipse.cdt.core.dom.ast.IASTImplicitName; +import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; import org.eclipse.cdt.core.dom.ast.IASTName; @@ -50,6 +54,8 @@ import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; import org.eclipse.cdt.core.dom.ast.IBasicType; @@ -57,7 +63,6 @@ import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; import org.eclipse.cdt.core.dom.ast.IEnumeration; -import org.eclipse.cdt.core.dom.ast.IEnumerator; import org.eclipse.cdt.core.dom.ast.IFunction; import org.eclipse.cdt.core.dom.ast.IParameter; import org.eclipse.cdt.core.dom.ast.IPointerType; @@ -73,6 +78,8 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; @@ -85,35 +92,40 @@ import org.eclipse.cdt.core.index.IIndexMacro; import org.eclipse.cdt.core.index.IndexFilter; /** - * The inclusion resolver finds the bindings which have to be included from a given AST. + * For a whole translation unit or a part of it determines a set of externally defined bindings that + * must be defined and a set of bindings that must be declared. */ public class BindingClassifier { private final InclusionContext fContext; - - /** Stores the AST node which this resolver is working on. */ - private final IASTNode fNode; - - /** Stores the bindings which require a full definition. */ - private final Set fBindingsToDefine; - - /** Stores the bindings which only require a simple forward declaration. */ - private final Set fBindingsToDeclare; private final IncludePreferences fPreferences; + /** The bindings which require a full definition. */ + private final Set fBindingsToDefine; + /** The bindings which only require a simple forward declaration. */ + private final Set fBindingsToDeclare; + /** The AST that the classifier is working on. */ + private IASTTranslationUnit fAst; + private final BindingCollector fBindingCollector; + private final Set fProcessedDefinedBindings; + private final Set fProcessedDeclaredBindings; /** - * Constructs a new inclusion resolver. - * * @param context the context for binding classification - * @param node the AST node which should be processed by the resolver */ - public BindingClassifier(InclusionContext context, IASTNode node) { + public BindingClassifier(InclusionContext context) { fContext = context; fPreferences = context.getPreferences(); - fNode = node; fBindingsToDefine = new HashSet(); fBindingsToDeclare = new HashSet(); - - processNode(fNode); + fProcessedDefinedBindings = new HashSet(); + fProcessedDeclaredBindings = new HashSet(); + fBindingCollector = new BindingCollector(); + } + + public void classifyNodeContents(IASTNode node) { + if (fAst == null) { + fAst = node.getTranslationUnit(); + } + node.accept(fBindingCollector); } /** @@ -130,650 +142,6 @@ public class BindingClassifier { return fBindingsToDeclare; } - /** - * Processes the given node and all of its children recursively. - * - * @param node The node to process. - */ - private void processNode(IASTNode node) { - if (processSingleNode(node)) { - // Process all children of this node as well. - IASTNode[] children = node.getChildren(); - for (IASTNode child : children) { - processNode(child); - } - } - } - - /** - * Processes the given node, but none of its children. - * - * @param node The node to process. - * @return Whether the children of the node must be processed. - */ - private boolean processSingleNode(IASTNode node) { - if (node == null) { - return false; - } - - if (node instanceof IASTSimpleDeclaration) { - /* - * The type specifier of a simple declaration of a variable must always be defined - * except within the situations shown by the following examples: - * - * Example 1: - * X* x; // definition of X is not required here - pointer type - * - * Example 2: - * X& x; // definition of X is not required here - reference type - * - * The type specifier of a simple function declaration also doesn't need to be defined: - * - * Example 3: - * X foo(); // definition of X is not required here - * - * The type specifier of static member declarations also doesn't need to be defined: - * - * Example 4: - * class Y { - * static X x; // definition of X is not required here - * }; - */ - IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) node; - IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); - IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); - - if (declSpecifier instanceof IASTNamedTypeSpecifier) { - // We only handle simple declarations here whose declaration specifiers are named - // type specifiers. - boolean staticMember = - simpleDeclaration.getParent() instanceof IASTCompositeTypeSpecifier && - declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static; - - // Declare the named type specifier if all declarators are either pointers, - // references or functions. - boolean canBeDeclared = true; - if (!staticMember) { - for (IASTDeclarator declarator : declarators) { - if (!(declarator instanceof IASTFunctionDeclarator) && - declarator.getPointerOperators().equals(IASTPointerOperator.EMPTY_ARRAY)) { - canBeDeclared = false; - break; - } - } - } - - if (!canBeDeclared) { - defineBinding(((IASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding()); - } - } - } else if (node instanceof ICPPASTBaseSpecifier) { - /* - * The type of a base specifier must always be defined. - * - * Example: - * class Y : X {}; // definition of X is required here - */ - defineBinding(((ICPPASTBaseSpecifier) node).getName().resolveBinding()); - } else if (node instanceof IASTInitializer) { - /* - * The type of the member of a constructor chain initializer doesn't need to be defined - * if it is a pointer or reference type and if the argument type matches. - * - * Example 1: - * class X { - * X* x1; - * X(X* x2) : - * x1(x2) {} // definition of typeof(x1) is not required here - * }; - * - * Example 2: - * class X { - * X& x1; - * X(X& x2) : - * x1(x2) {} // definition of typeof(x1) is not required here - * }; - * - * The type of a constructor initializer doesn't need to be defined if it matches with - * the type of the declaration. - * - * Example: - * void foo(X& x1) { - * X& x2(x1); // definition of typeof(x1) is not required here - * } - * - * The type of an equals initializer doesn't need to be defined if it matches with - * the type of the declaration. - * - * Example: - * void foo(X& x1) { - * X& x2 = x1; // definition of typeof(x1) is not required here - * } - */ - - IASTInitializer initializer = (IASTInitializer) node; - - // Get the binding of the initialized AST name first. - IASTNode memberNode = node; - IASTName memberName = null; - IBinding memberBinding = null; - - while (memberNode != null) { - if (memberNode instanceof IASTDeclarator) { - memberName = ((IASTDeclarator) memberNode).getName(); - break; - } else if (memberNode instanceof ICPPASTConstructorChainInitializer) { - memberName = ((ICPPASTConstructorChainInitializer) memberNode).getMemberInitializerId(); - break; - } - memberNode = memberNode.getParent(); - } - if (memberName != null) { - memberBinding = memberName.resolveBinding(); - } - - // Get the arguments of the initializer. - IASTInitializerClause[] actualParameters = new IASTInitializerClause[] { }; - if (initializer instanceof ICPPASTConstructorInitializer) { - ICPPASTConstructorInitializer constructorInitializer = (ICPPASTConstructorInitializer) initializer; - actualParameters = constructorInitializer.getArguments(); - } else if (initializer instanceof IASTEqualsInitializer) { - IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer; - actualParameters = new IASTInitializerClause[] { equalsInitializer.getInitializerClause() }; - } - - if (memberBinding instanceof IVariable) { - // Variable construction. - IType memberType = ((IVariable) memberBinding).getType(); - if (!(memberType instanceof IPointerType) && !(memberType instanceof ICPPReferenceType)) { - // We're constructing a non-pointer type. We need to define the member type - // either way since we must be able to call its constructor. - defineTypeExceptTypedef(memberType); - - // TODO: Process the arguments. But how to get the corresponding IParameter[] array here? - // processParameters(declaredParameters, arguments); - } else { - // We're constructing a pointer type. No constructor is called. We however have - // to check whether the argument type matches the declared type. - memberType = getNestedType(memberType, REF); - for (IASTInitializerClause actualParameter : actualParameters) { - if (actualParameter instanceof IASTExpression) { - IType parameterType = ((IASTExpression) actualParameter).getExpressionType(); - if (!isSameType(memberType, parameterType)) { - // Types don't match. Define both types. - defineTypeExceptTypedef(memberType); - defineTypeExceptTypedef(parameterType); - } - } - } - } - } else if (memberBinding instanceof ICPPConstructor) { - // Class construction - ICPPConstructor constructor = (ICPPConstructor) memberBinding; - - // We need to define the owning type of the constructor. - defineBinding(constructor.getOwner()); - - // Process the parameters. - processParameters(constructor.getParameters(), actualParameters); - } - - } else if (node instanceof IASTElaboratedTypeSpecifier) { - /* - * The type specifier of an elaborated type neither needs to be defined nor needs to be - * declared. This is because an elaborated type specifier is a self-sufficient - * statement. - * - * Example: - * class X; // neither definition nor declaration of X is required here - */ - return false; - } else if (node instanceof IASTFunctionDefinition) { - /* - * The type specifier of a function definition doesn't need to be defined if it is - * a pointer or reference type. - * - * Example 1: - * X *foo() { } // definition of X is not required here - * - * Example 2: - * X& foo() { } // definition of X is not required here - */ - IBinding binding = ((IASTFunctionDefinition) node).getDeclarator().getName().resolveBinding(); - if (binding instanceof IFunction) { - IFunction function = (IFunction) binding; - - // Define the return type if necessary - IType returnType = function.getType().getReturnType(); - if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { - defineTypeExceptTypedef(returnType); - } - - // Define parameter types if necessary - IType[] parameterTypes = function.getType().getParameterTypes(); - for (IType type : parameterTypes) { - if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) { - defineTypeExceptTypedef(type); - } - } - } - } else if (node instanceof IASTIfStatement || - node instanceof IASTForStatement || - node instanceof IASTWhileStatement || - node instanceof IASTDoStatement) { - IASTExpression conditionExpression = null; - - if (node instanceof IASTIfStatement) { - /* - * The type of the condition expression of an if statement doesn't need to be - * defined if it's a pointer type. - * - * Example: - * void foo(X* x) { - * if (x) { } // definition of typeof(x) is not required here - * } - */ - conditionExpression = ((IASTIfStatement) node).getConditionExpression(); - } else if (node instanceof IASTForStatement) { - /* - * The type of the condition expression of a for statement doesn't need to be - * defined if it's a pointer type. - * - * Example: - * void foo(X* x) { - * for (; x; ) { } // definition of typeof(x) is not required here - * } - */ - conditionExpression = ((IASTForStatement) node).getConditionExpression(); - } else if (node instanceof IASTWhileStatement) { - /* - * The type of the condition expression of a while statement doesn't need to be - * defined if it's a pointer type. - * - * Example: - * void foo(X* x) { - * while (x) { } // definition of typeof(x) is not required here - * } - */ - conditionExpression = ((IASTWhileStatement) node).getCondition(); - } else if (node instanceof IASTDoStatement) { - /* - * The type of the condition expression of a do statement doesn't need to be - * defined if it's a pointer type. - * - * Example: - * void foo(X* x) { - * do { } while (x); // definition of typeof(x) is not required here - * } - */ - conditionExpression = ((IASTDoStatement) node).getCondition(); - } - - if (conditionExpression != null) { - IType conditionExpressionType = conditionExpression.getExpressionType(); - if (!(conditionExpressionType instanceof IPointerType)) { - defineTypeExceptTypedef(conditionExpressionType); - } - } - } else if (node instanceof IASTReturnStatement) { - /* - * The type of the return value expression doesn't need to be defined if the actual - * return type matches the declared return type (i.e. if no implicit conversion is - * necessary). - * - * Example: - * X& foo(X& x) { - * return x; // definition of typeof(x) is not required here - * } - */ - IASTReturnStatement returnStatement = (IASTReturnStatement) node; - - IASTExpression returnValue = returnStatement.getReturnValue(); - if (returnValue != null) { - // Get the containing function definition. - IASTNode functionDefinitionNode = returnStatement; - while (functionDefinitionNode != null && !(functionDefinitionNode instanceof IASTFunctionDefinition)) { - functionDefinitionNode = functionDefinitionNode.getParent(); - } - - // Check whether the declared return type matches the actual return type. - if (functionDefinitionNode != null) { - IASTFunctionDefinition functionDefinition = (IASTFunctionDefinition) functionDefinitionNode; - IASTFunctionDeclarator functionDeclarator = functionDefinition.getDeclarator(); - if (functionDeclarator != null) { - IBinding binding = functionDeclarator.getName().resolveBinding(); - if (binding instanceof IFunction) { - IFunction function = (IFunction) binding; - - // Get the declared return type and the actual expression type. - // Don't care about reference types since they can be converted into - // non-reference types and vice versa without requiring a definition. - IType returnType = function.getType().getReturnType(); - returnType = getNestedType(returnType, REF); - IType expressionType = getNestedType(returnValue.getExpressionType(), REF); - - // Compare the two types. - if (!isSameType(returnType, expressionType)) { - // Not the same type. Define both types. - defineTypeExceptTypedef(returnType); - defineTypeExceptTypedef(expressionType); - } - } - } - } - } - } else if (node instanceof IASTIdExpression) { - /* - * The type of an identifier expression doesn't need to be defined if it's a pointer or - * a reference type. - * - * Example: - * void foo(X& x) { - * x; // definition of typeof(x) is not required here - * } - */ - IASTIdExpression idExpression = (IASTIdExpression) node; - - IBinding binding = idExpression.getName().resolveBinding(); - if (binding instanceof IVariable) { - // Get the declared type. - IType expressionType = ((IVariable) binding).getType(); - if (!(expressionType instanceof IPointerType) && !(expressionType instanceof ICPPReferenceType)) { - defineTypeExceptTypedef(expressionType); - } - } - } else if (node instanceof IASTUnaryExpression) { - /* - * The type of the operand of an unary expression doesn't need to be defined if - * the operator is the ampersand operator: - * - * Example: - * void foo(X& x) { - * &x; // ampersand operator - * } - * - * If the operand is a pointer type, the following operators also don't require - * a definition: - * - * Example: - * void foo(X* x) { - * __alignof(x); // alignof operator - * !x; // not operator - * +x; // unary plus operator - * sizeof(x); // sizeof operator - * typeid(x); // typeid operator - * } - */ - IASTUnaryExpression unaryExpression = (IASTUnaryExpression) node; - - boolean expressionDefinitionRequired = true; - switch (unaryExpression.getOperator()) { - case IASTUnaryExpression.op_amper: - case IASTUnaryExpression.op_bracketedPrimary: - // The ampersand operator as well as brackets never require a definition. - expressionDefinitionRequired = false; - break; - case IASTUnaryExpression.op_alignOf: - case IASTUnaryExpression.op_not: - case IASTUnaryExpression.op_plus: - case IASTUnaryExpression.op_sizeof: - case IASTUnaryExpression.op_typeid: - // If the operand is a pointer type, then it doesn't need to be defined. - if (unaryExpression.getOperand().getExpressionType() instanceof IPointerType) { - expressionDefinitionRequired = false; - } - } - - if (expressionDefinitionRequired) { - defineTypeExceptTypedef(unaryExpression.getOperand().getExpressionType()); - } - } else if (node instanceof IASTBinaryExpression) { - /* - * The types of the operands of a binary expression don't need to be defined for - * the following operators if the operands are pointer types: - * - * Example: - * void foo(X* x) { - * x = x; // assignment operator - * x == x; // equals operator - * x != x; // not equals operator - * x >= x; // greater equal operator - * x > x; // greater operator - * x <= x; // less equal operator - * x < x; // less operator - * x && x; // logical and operator - * x || x; // logical or operator - * } - * - * However, note that if both operands are pointers of different types, then only - * the following operators don't require a definition of the types of the operands: - * - * void foo(X* x, Y* y) { - * x && y; // logical and operator - * x || y; // logical or operator - * } - */ - - IASTBinaryExpression binaryExpression = (IASTBinaryExpression) node; - - IType operand1Type = binaryExpression.getOperand1().getExpressionType(); - IType operand2Type = binaryExpression.getOperand2().getExpressionType(); - - boolean expression1DefinitionRequired = true; - boolean expression2DefinitionRequired = true; - - switch (binaryExpression.getOperator()) { - case IASTBinaryExpression.op_logicalAnd: - case IASTBinaryExpression.op_logicalOr: - // Pointer types don't need to be defined for logical operations. - if (operand1Type instanceof IPointerType) { - expression1DefinitionRequired = false; - } - if (operand2Type instanceof IPointerType) { - expression2DefinitionRequired = false; - } - break; - case IASTBinaryExpression.op_assign: - case IASTBinaryExpression.op_equals: - case IASTBinaryExpression.op_notequals: - case IASTBinaryExpression.op_greaterEqual: - case IASTBinaryExpression.op_greaterThan: - case IASTBinaryExpression.op_lessEqual: - case IASTBinaryExpression.op_lessThan: - // If both operands are identical pointer types, then they don't need to be - // defined. - if (operand1Type instanceof IPointerType && operand2Type instanceof IPointerType) { - if (isSameType(operand1Type, operand2Type)) { - expression1DefinitionRequired = false; - expression2DefinitionRequired = false; - } - } else if (operand1Type instanceof IPointerType) { - // Only the first operand is a pointer type. It doesn't have to be defined. - expression1DefinitionRequired = false; - } else if (operand2Type instanceof IPointerType) { - // Only the second operand is a pointer type. It doesn't have to be defined. - expression2DefinitionRequired = false; - } - } - - if (expression1DefinitionRequired) { - defineTypeExceptTypedef(operand1Type); - } - if (expression2DefinitionRequired) { - defineTypeExceptTypedef(operand2Type); - } - } else if (node instanceof IASTConditionalExpression) { - /* - * The type of the condition of a conditional expression doesn't need to be defined - * if it's a pointer type. - * - * Example: - * void foo(X* x) { - * x ? 1 : 0; // definition of typeof(x) is not required here - * } - */ - - IASTConditionalExpression conditionalExpression = (IASTConditionalExpression) node; - IASTExpression logicalConditionExpression = conditionalExpression.getLogicalConditionExpression(); - - if (logicalConditionExpression != null) { - IType logicalConditionExpressionType = logicalConditionExpression.getExpressionType(); - if (!(logicalConditionExpressionType instanceof IPointerType)) { - defineTypeExceptTypedef(logicalConditionExpressionType); - } - } - } else if (node instanceof IASTFunctionCallExpression) { - /* - * The return type and argument types of a function call expression don't need to be - * defined if they're pointer or reference types. The declared and actual types of - * the arguments must further be identical, since implicit type conversions require - * a definition of both the source and the target type. - * - * Example: - * X& foo(X& x); - * void bar(X& x) { - * foo(x); // definition of typeof(foo) and typeof(x) is not required here - * } - * - * Also note that the function call itself doesn't require a definition as long as - * it's not a constructor call: - * - * Example 1: - * void foo() { - * bar(); // definition of bar() is not required here (assuming bar is a function) - * } - * - * Example 2: - * void foo() { - * X(); // definition of X is required here (assuming X is a composite type) - * } - */ - IASTFunctionCallExpression functionCallExpression = (IASTFunctionCallExpression) node; - IASTExpression functionNameExpression = functionCallExpression.getFunctionNameExpression(); - - if (functionNameExpression instanceof IASTIdExpression) { - IBinding binding = ((IASTIdExpression) functionNameExpression).getName().resolveBinding(); - if (binding instanceof IFunction) { - IFunction function = (IFunction) binding; - // Handle return or expression type of the function or constructor call. - IType returnType = function.getType().getReturnType(); - if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { - // The return type needs a full definition. - defineTypeExceptTypedef(returnType); - } - - // Handle parameters. - processParameters(function.getParameters(), functionCallExpression.getArguments()); - } else if (binding instanceof IType) { - // The binding resolves to a composite type, as it does for constructor calls. - // We have to define the binding. - defineTypeExceptTypedef((IType) binding); - } - } - } else if (node instanceof IASTFieldReference) { - /* - * The type of the expression part of a field reference always requires a definition. - * - * Example: - * void foo(X& x1, X* x2) { - * x1.bar(); // definition of typeof(x1) is required here - * x2->bar(); // definition of typeof(x2) is required here - * } - */ - - defineTypeExceptTypedef(((IASTFieldReference) node).getFieldOwner().getExpressionType()); - } else if (node instanceof ICPPASTNewExpression) { - /* - * The type specifier of a "new" expression always requires a definition. - * - * Example: - * void foo() { - * new X(); // definition of X is required here - * } - */ - defineTypeExceptTypedef(((ICPPASTNewExpression) node).getExpressionType()); - } else if (node instanceof ICPPASTDeleteExpression) { - /* - * The expression type of a "delete" expression always requires a full definition. - * This is necessary because the compiler needs to be able to call the destructor. - * - * Example: - * void foo(X* x) { - * delete x; // definition of typeof(x) is required here - * } - */ - defineTypeExceptTypedef(((ICPPASTDeleteExpression) node).getOperand().getExpressionType()); - } else if (node instanceof IASTCastExpression) { - /* - * Explicit type casts always need the definition of the underlying types. - * - * Example: - * void foo(X* x) { - * (Y*) x; // definition of both Y and typeof(x) is required here - * } - */ - IASTCastExpression castExpression = (IASTCastExpression) node; - IType targetType = castExpression.getExpressionType(); - IType sourceType = castExpression.getOperand().getExpressionType(); - - if (!isSameType(targetType, sourceType)) { - // Source and target types of the cast expression are different. We need to define - // both types, even if they're pointers. - defineTypeExceptTypedef(targetType); - defineTypeExceptTypedef(sourceType); - } else if (!(targetType instanceof IPointerType) && !(targetType instanceof ICPPReferenceType)) { - // Define the target type if it's not a pointer or reference type. - defineTypeExceptTypedef(targetType); - } - } else if (node instanceof ICPPASTCatchHandler) { - /* - * Catch handles always need the definition of the caught type, even if it's a pointer - * or reference type. - * - * Example: - * void foo() { - * try { - * } catch (X& x) { // Definition of X is required here - * } - * } - */ - ICPPASTCatchHandler catchHandler = (ICPPASTCatchHandler) node; - IASTDeclaration declaration = catchHandler.getDeclaration(); - if (declaration instanceof IASTSimpleDeclaration) { - IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; - IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); - if (declSpecifier instanceof IASTNamedTypeSpecifier) { - IASTNamedTypeSpecifier namedTypeSpecifier = (IASTNamedTypeSpecifier) declSpecifier; - defineBinding(namedTypeSpecifier.getName().resolveBinding()); - } - } - } else if (node instanceof IASTName) { - // We've found an AST name. Add it to the bindings which can be declared (we assume - // that all bindings which have to be defined are already explicitly handled by - // the code above). - IASTName name = (IASTName) node; - - if (name instanceof ICPPASTQualifiedName) { - // Any qualifying names must be defined. - IASTName[] names = ((ICPPASTQualifiedName) name).getNames(); - for (int i = 0; i + 1 < names.length; i++) { - defineBinding(names[i].resolveBinding()); - } - } - - IBinding binding = name.resolveBinding(); - IBinding owner = binding.getOwner(); - if (owner instanceof IType) { - defineBinding(owner); // Member access requires definition of the containing type. - } else { - declareBinding(binding); // Declare the binding of this name. - } - } - - // Return true to signal that all children of this node must be processed as well. - return true; - } - /** * Defines the required types of the parameters of a function or constructor call expression by * comparing the declared parameters with the actual arguments. @@ -813,8 +181,8 @@ public class BindingClassifier { } else { // Both the type of the declared parameter as well as the type of the actual // parameter require a full definition. - defineTypeExceptTypedef(declaredParameterType); - defineTypeExceptTypedef(actualParameterType); + defineTypeExceptTypedefOrNonFixedEnum(declaredParameterType); + defineTypeExceptTypedefOrNonFixedEnum(actualParameterType); } } } @@ -851,16 +219,23 @@ public class BindingClassifier { * @return The binding(s) which is/are suitable for either declaration or definition, * or an empty list if no such binding is available. */ - private List resolveBinding(IBinding binding) { - if (binding instanceof IVariable) { + private List getRequiredBindings(IBinding binding) { + List bindings = new ArrayList(); + + if (binding instanceof ICPPMember) { + // If the binding is a member, get its owning composite type. + binding = binding.getOwner(); + } else if (binding instanceof IVariable) { + if (binding instanceof ICPPSpecialization) { + bindings.add(((ICPPSpecialization) binding).getSpecializedBinding()); + } else { + bindings.add(binding); + } // Resolve the type of the variable. binding = getTypeBinding(((IVariable) binding).getType()); } else if (binding instanceof IType) { // Resolve the type. binding = getTypeBinding((IType) binding); - } else if (binding instanceof ICPPConstructor) { - // If the binding is a constructor, get its owning composite type. - binding = binding.getOwner(); } else if (binding instanceof ICPPNamespace) { // Namespaces are neither declared nor defined. binding = null; @@ -873,8 +248,6 @@ public class BindingClassifier { binding = ((ICPPSpecialization) binding).getSpecializedBinding(); } - List bindings = new ArrayList(); - if (binding instanceof IProblemBinding) { IProblemBinding problemBinding = (IProblemBinding) binding; @@ -929,35 +302,58 @@ public class BindingClassifier { * @param binding The binding to add. */ private void declareBinding(IBinding binding) { - List resolvedBindings = resolveBinding(binding); + if (fProcessedDefinedBindings.contains(binding)) + return; - for (IBinding resolvedBinding : resolvedBindings) { - if (fBindingsToDeclare.contains(resolvedBinding) || fBindingsToDefine.contains(resolvedBinding)) { + if (!canForwardDeclare(binding)) + defineBinding(binding); + + if (!fProcessedDeclaredBindings.add(binding)) + return; + + List requiredBindings = getRequiredBindings(binding); + + for (IBinding requiredBinding : requiredBindings) { + if (fBindingsToDeclare.contains(requiredBinding) || fBindingsToDefine.contains(requiredBinding)) { return; } - - // Check whether the user actually wants to declare this binding. - boolean doDeclareBinding = true; - if (resolvedBinding instanceof ICompositeType) { - doDeclareBinding = fPreferences.forwardDeclareCompositeTypes; - } else if (resolvedBinding instanceof IEnumeration) { - doDeclareBinding = fPreferences.forwardDeclareEnums; - } else if (resolvedBinding instanceof IFunction && !(resolvedBinding instanceof ICPPMethod)) { - doDeclareBinding = fPreferences.forwardDeclareFunctions; - } else if (resolvedBinding instanceof IIndexMacro || resolvedBinding instanceof ITypedef - || resolvedBinding instanceof IEnumerator) { - // Macros, typedefs and enumerators can never be declared. - doDeclareBinding = false; + if (fAst.getDefinitionsInAST(requiredBinding).length != 0) { + return; // Defined locally + } + if (fAst.getDeclarationsInAST(requiredBinding).length != 0) { + return; // Defined locally } - if (doDeclareBinding) { - fBindingsToDeclare.add(resolvedBinding); + if (canForwardDeclare(requiredBinding)) { + fBindingsToDeclare.add(requiredBinding); } else { - fBindingsToDefine.add(resolvedBinding); + fBindingsToDefine.add(requiredBinding); } } } + /** + * Checks whether the binding can be forward declared or not. + */ + private boolean canForwardDeclare(IBinding binding) { + boolean canDeclare = false; + if (binding instanceof ICompositeType) { + canDeclare = fPreferences.forwardDeclareCompositeTypes; + } else if (binding instanceof IEnumeration) { + canDeclare = fPreferences.forwardDeclareEnums; + } else if (binding instanceof IFunction && !(binding instanceof ICPPMethod)) { + canDeclare = fPreferences.forwardDeclareFunctions; + } else if (binding instanceof IVariable) { + canDeclare = fPreferences.forwardDeclareExternalVariables; + } + + if (canDeclare && !fPreferences.forwardDeclareTemplates + && binding instanceof ICPPTemplateDefinition) { + canDeclare = false; + } + return canDeclare; + } + /** * Adds the given type to the list of bindings which have to be declared. * @@ -970,34 +366,47 @@ public class BindingClassifier { } /** - * Adds the given type to the list of bindings which have to be defined. Typedefs are skipped - * since they must be defined in the file that references them by name. If a typedef is - * explicitly referenced in this translation unit, it will be defined independently from this - * method. + * Adds the given type to the list of bindings which have to be defined. Typedefs and + * enumerations without fixed underlying type are skipped since they must be defined in the file + * that references them by name. If the type is explicitly referenced in this translation unit, + * it will be defined independently from this method. * * @param type The type to add. */ - private void defineTypeExceptTypedef(IType type) { + private void defineTypeExceptTypedefOrNonFixedEnum(IType type) { IBinding typeBinding = getTypeBinding(type); - if (typeBinding != null && !(typeBinding instanceof ITypedef)) { + if (typeBinding != null && !(typeBinding instanceof ITypedef) + && !isEnumerationWithoutFixedUnderlyingType(typeBinding)) { defineBinding(typeBinding); } } + private boolean isEnumerationWithoutFixedUnderlyingType(IBinding typeBinding) { + return typeBinding instanceof IEnumeration + && (!(typeBinding instanceof ICPPEnumeration) || ((ICPPEnumeration) typeBinding).getFixedType() == null); + } + /** * Adds the given binding to the list of bindings which have to be defined. * * @param binding The binding to add. */ private void defineBinding(IBinding binding) { + if (!fProcessedDefinedBindings.add(binding)) + return; + + if (fAst.getDefinitionsInAST(binding).length != 0) { + return; // Defined locally + } + if (binding instanceof ICPPTemplateInstance) { defineTemplateArguments((ICPPTemplateInstance) binding); } - List resolvedBindings = resolveBinding(binding); - for (IBinding resolvedBinding : resolvedBindings) { - fBindingsToDeclare.remove(resolvedBinding); - fBindingsToDefine.add(resolvedBinding); + List requiredBindings = getRequiredBindings(binding); + for (IBinding requiredBinding : requiredBindings) { + fBindingsToDeclare.remove(requiredBinding); + fBindingsToDefine.add(requiredBinding); } } @@ -1037,4 +446,608 @@ public class BindingClassifier { } } } + + private void declareFunction(IFunction function, IASTFunctionCallExpression functionCallExpression) { + // Handle return or expression type of the function or constructor call. + IType returnType = function.getType().getReturnType(); + if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { + // The return type needs a full definition. + defineTypeExceptTypedefOrNonFixedEnum(returnType); + } + + // Handle parameters. + processParameters(function.getParameters(), functionCallExpression.getArguments()); + } + + private class BindingCollector extends ASTVisitor { + BindingCollector() { + super(true); + shouldVisitImplicitNames = true; + } + + @Override + public int visit(IASTDeclaration declaration) { + if (declaration instanceof IASTSimpleDeclaration) { + /* + * The type specifier of a simple declaration of a variable must always be defined + * except within the situations shown by the following examples: + * + * Example 1: + * X* x; // definition of X is not required here - pointer type + * + * Example 2: + * X& x; // definition of X is not required here - reference type + * + * The type specifier of a simple function declaration also doesn't need to be + * defined: + * + * Example 3: + * X foo(); // definition of X is not required here + * + * The type specifier of static member declarations also doesn't need to be defined: + * + * Example 4: + * class Y { + * static X x; // definition of X is not required here + * }; + */ + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; + IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); + IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); + + if (declSpecifier instanceof IASTNamedTypeSpecifier) { + // We only handle simple declarations here whose declaration specifiers are + // named type specifiers. + boolean staticMember = + simpleDeclaration.getParent() instanceof IASTCompositeTypeSpecifier && + declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static; + + // Declare the named type specifier if all declarators are either pointers, + // references or functions. + boolean canBeDeclared = true; + if (!staticMember) { + for (IASTDeclarator declarator : declarators) { + if (!(declarator instanceof IASTFunctionDeclarator) && + declarator.getPointerOperators().equals(IASTPointerOperator.EMPTY_ARRAY)) { + canBeDeclared = false; + break; + } + } + } + + if (!canBeDeclared) { + defineBinding(((IASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding()); + } + } + } else if (declaration instanceof IASTFunctionDefinition) { + /* + * The type specifier of a function definition doesn't need to be defined if it is + * a pointer or reference type. + * + * Example 1: + * X *foo() { } // definition of X is not required here + * + * Example 2: + * X& foo() { } // definition of X is not required here + */ + IBinding binding = ((IASTFunctionDefinition) declaration).getDeclarator().getName().resolveBinding(); + if (binding instanceof IFunction) { + IFunction function = (IFunction) binding; + + // Define the return type if necessary + IType returnType = function.getType().getReturnType(); + if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) { + defineTypeExceptTypedefOrNonFixedEnum(returnType); + } + + // Define parameter types if necessary + IType[] parameterTypes = function.getType().getParameterTypes(); + for (IType type : parameterTypes) { + if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) { + defineTypeExceptTypedefOrNonFixedEnum(type); + } + } + } + } + return PROCESS_CONTINUE; + } + + @Override + public int visit(ICPPASTBaseSpecifier baseSpecifier) { + /* + * The type of a base specifier must always be defined. + * + * Example: + * class Y : X {}; // definition of X is required here + */ + defineBinding(baseSpecifier.getName().resolveBinding()); + return PROCESS_CONTINUE; + } + + @Override + public int visit(IASTInitializer initializer) { + /* + * The type of the member of a constructor chain initializer doesn't need to be defined + * if it is a pointer or reference type and if the argument type matches. + * + * Example 1: + * class X { + * X* x1; + * X(X* x2) : + * x1(x2) {} // definition of typeof(x1) is not required here + * }; + * + * Example 2: + * class X { + * X& x1; + * X(X& x2) : + * x1(x2) {} // definition of typeof(x1) is not required here + * }; + * + * The type of a constructor initializer doesn't need to be defined if it matches with + * the type of the declaration. + * + * Example: + * void foo(X& x1) { + * X& x2(x1); // definition of typeof(x1) is not required here + * } + * + * The type of an equals initializer doesn't need to be defined if it matches with + * the type of the declaration. + * + * Example: + * void foo(X& x1) { + * X& x2 = x1; // definition of typeof(x1) is not required here + * } + */ + + // Get the binding of the initialized AST name first. + IASTNode memberNode = initializer; + IASTName memberName = null; + IBinding memberBinding = null; + + while (memberNode != null) { + if (memberNode instanceof IASTDeclarator) { + memberName = ((IASTDeclarator) memberNode).getName(); + break; + } else if (memberNode instanceof ICPPASTConstructorChainInitializer) { + memberName = ((ICPPASTConstructorChainInitializer) memberNode).getMemberInitializerId(); + break; + } + memberNode = memberNode.getParent(); + } + if (memberName != null) { + memberBinding = memberName.resolveBinding(); + } + + // Get the arguments of the initializer. + IASTInitializerClause[] actualParameters = new IASTInitializerClause[] { }; + if (initializer instanceof ICPPASTConstructorInitializer) { + ICPPASTConstructorInitializer constructorInitializer = (ICPPASTConstructorInitializer) initializer; + actualParameters = constructorInitializer.getArguments(); + } else if (initializer instanceof IASTEqualsInitializer) { + IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer; + actualParameters = new IASTInitializerClause[] { equalsInitializer.getInitializerClause() }; + } + + if (memberBinding instanceof IVariable) { + // Variable construction. + IType memberType = ((IVariable) memberBinding).getType(); + if (!(memberType instanceof IPointerType) && !(memberType instanceof ICPPReferenceType)) { + // We're constructing a non-pointer type. We need to define the member type + // either way since we must be able to call its constructor. + defineTypeExceptTypedefOrNonFixedEnum(memberType); + + // TODO: Process the arguments. But how to get the corresponding IParameter[] array here? + // processParameters(declaredParameters, arguments); + } else { + // We're constructing a pointer type. No constructor is called. We however have + // to check whether the argument type matches the declared type. + memberType = getNestedType(memberType, REF); + for (IASTInitializerClause actualParameter : actualParameters) { + if (actualParameter instanceof IASTExpression) { + IType parameterType = ((IASTExpression) actualParameter).getExpressionType(); + if (!isSameType(memberType, parameterType)) { + // Types don't match. Define both types. + defineTypeExceptTypedefOrNonFixedEnum(memberType); + defineTypeExceptTypedefOrNonFixedEnum(parameterType); + } + } + } + } + } else if (memberBinding instanceof ICPPConstructor) { + // Class construction + ICPPConstructor constructor = (ICPPConstructor) memberBinding; + + // We need to define the owning type of the constructor. + defineBinding(constructor.getOwner()); + + // Process the parameters. + processParameters(constructor.getParameters(), actualParameters); + } + return PROCESS_CONTINUE; + } + + @Override + public int visit(IASTDeclSpecifier declSpec) { + if (declSpec instanceof IASTElaboratedTypeSpecifier) { + /* + * The type specifier of an elaborated type neither needs to be defined nor needs to be + * declared. This is because an elaborated type specifier is a self-sufficient + * statement. + * + * Example: + * class X; // neither definition nor declaration of X is required here + */ + return PROCESS_SKIP; + } + return PROCESS_CONTINUE; + } + + @Override + public int visit(IASTStatement statement) { + if (statement instanceof IASTReturnStatement) { + /* + * The type of the return value expression doesn't need to be defined if the actual + * return type matches the declared return type (i.e. if no implicit conversion is + * necessary). + * + * Example: + * X& foo(X& x) { + * return x; // definition of typeof(x) is not required here + * } + */ + IASTReturnStatement returnStatement = (IASTReturnStatement) statement; + + IASTExpression returnValue = returnStatement.getReturnValue(); + if (returnValue != null) { + // Get the containing function definition. + IASTNode functionDefinitionNode = returnStatement; + while (functionDefinitionNode != null && !(functionDefinitionNode instanceof IASTFunctionDefinition)) { + functionDefinitionNode = functionDefinitionNode.getParent(); + } + + // Check whether the declared return type matches the actual return type. + if (functionDefinitionNode != null) { + IASTFunctionDefinition functionDefinition = (IASTFunctionDefinition) functionDefinitionNode; + IASTFunctionDeclarator functionDeclarator = functionDefinition.getDeclarator(); + if (functionDeclarator != null) { + IBinding binding = functionDeclarator.getName().resolveBinding(); + if (binding instanceof IFunction) { + IFunction function = (IFunction) binding; + + // Get the declared return type and the actual expression type. + // Don't care about reference types since they can be converted into + // non-reference types and vice versa without requiring a definition. + IType returnType = function.getType().getReturnType(); + returnType = getNestedType(returnType, REF); + IType expressionType = getNestedType(returnValue.getExpressionType(), REF); + + // Compare the two types. + if (!isSameType(returnType, expressionType)) { + // Not the same type. Define both types. + defineTypeExceptTypedefOrNonFixedEnum(returnType); + defineTypeExceptTypedefOrNonFixedEnum(expressionType); + } + } + } + } + } + } else if (statement instanceof ICPPASTCatchHandler) { + /* + * Catch handles always need the definition of the caught type, even if it's a pointer + * or reference type. + * + * Example: + * void foo() { + * try { + * } catch (X& x) { // Definition of X is required here + * } + * } + */ + ICPPASTCatchHandler catchHandler = (ICPPASTCatchHandler) statement; + IASTDeclaration declaration = catchHandler.getDeclaration(); + if (declaration instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; + IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); + if (declSpecifier instanceof IASTNamedTypeSpecifier) { + IASTNamedTypeSpecifier namedTypeSpecifier = (IASTNamedTypeSpecifier) declSpecifier; + defineBinding(namedTypeSpecifier.getName().resolveBinding()); + } + } + } + return PROCESS_CONTINUE; + } + + @Override + public int visit(IASTExpression expression) { + ASTNodeProperty propertyInParent = expression.getPropertyInParent(); + if (propertyInParent == IASTIfStatement.CONDITION + || propertyInParent == IASTForStatement.CONDITION + || propertyInParent == IASTWhileStatement.CONDITIONEXPRESSION + || propertyInParent == IASTDoStatement.CONDITION + || propertyInParent == IASTConditionalExpression.LOGICAL_CONDITION) { + /* + * The type of the condition expression doesn't need to be defined if it's + * a pointer type. + * + * Example: + * void foo(X* x) { + * if (x) { } // definition of typeof(x) is not required here + * } + */ + IType conditionExpressionType = expression.getExpressionType(); + if (!(conditionExpressionType instanceof IPointerType)) { + defineTypeExceptTypedefOrNonFixedEnum(conditionExpressionType); + } + } + + if (expression instanceof IASTIdExpression) { + /* + * The type of an identifier expression doesn't need to be defined if it's a pointer + * or a reference type. + * + * Example: + * void foo(X& x) { + * x; // definition of typeof(x) is not required here + * } + */ + IASTIdExpression idExpression = (IASTIdExpression) expression; + + IBinding binding = idExpression.getName().resolveBinding(); + if (binding instanceof IVariable) { + // Get the declared type. + IType expressionType = ((IVariable) binding).getType(); + if (!(expressionType instanceof IPointerType) && !(expressionType instanceof ICPPReferenceType)) { + defineTypeExceptTypedefOrNonFixedEnum(expressionType); + } + } + } else if (expression instanceof IASTUnaryExpression) { + /* + * The type of the operand of an unary expression doesn't need to be defined if + * the operator is the ampersand operator: + * + * Example: + * void foo(X& x) { + * &x; // ampersand operator + * } + * + * If the operand is a pointer type, the following operators also don't require + * a definition: + * + * Example: + * void foo(X* x) { + * __alignof(x); // alignof operator + * !x; // not operator + * +x; // unary plus operator + * sizeof(x); // sizeof operator + * typeid(x); // typeid operator + * } + */ + IASTUnaryExpression unaryExpression = (IASTUnaryExpression) expression; + + boolean expressionDefinitionRequired = true; + switch (unaryExpression.getOperator()) { + case IASTUnaryExpression.op_amper: + case IASTUnaryExpression.op_bracketedPrimary: + // The ampersand operator as well as brackets never require a definition. + expressionDefinitionRequired = false; + break; + case IASTUnaryExpression.op_alignOf: + case IASTUnaryExpression.op_not: + case IASTUnaryExpression.op_plus: + case IASTUnaryExpression.op_sizeof: + case IASTUnaryExpression.op_typeid: + // If the operand is a pointer type, then it doesn't need to be defined. + if (unaryExpression.getOperand().getExpressionType() instanceof IPointerType) { + expressionDefinitionRequired = false; + } + } + + if (expressionDefinitionRequired) { + defineTypeExceptTypedefOrNonFixedEnum(unaryExpression.getOperand().getExpressionType()); + } + } else if (expression instanceof IASTBinaryExpression) { + /* + * The types of the operands of a binary expression don't need to be defined for + * the following operators if the operands are pointer types: + * + * Example: + * void foo(X* x) { + * x = x; // assignment operator + * x == x; // equals operator + * x != x; // not equals operator + * x >= x; // greater equal operator + * x > x; // greater operator + * x <= x; // less equal operator + * x < x; // less operator + * x && x; // logical and operator + * x || x; // logical or operator + * } + * + * However, note that if both operands are pointers of different types, then only + * the following operators don't require a definition of the types of the operands: + * + * void foo(X* x, Y* y) { + * x && y; // logical and operator + * x || y; // logical or operator + * } + */ + IASTBinaryExpression binaryExpression = (IASTBinaryExpression) expression; + + IType operand1Type = binaryExpression.getOperand1().getExpressionType(); + IType operand2Type = binaryExpression.getOperand2().getExpressionType(); + + boolean expression1DefinitionRequired = true; + boolean expression2DefinitionRequired = true; + + switch (binaryExpression.getOperator()) { + case IASTBinaryExpression.op_logicalAnd: + case IASTBinaryExpression.op_logicalOr: + // Pointer types don't need to be defined for logical operations. + if (operand1Type instanceof IPointerType) { + expression1DefinitionRequired = false; + } + if (operand2Type instanceof IPointerType) { + expression2DefinitionRequired = false; + } + break; + case IASTBinaryExpression.op_assign: + case IASTBinaryExpression.op_equals: + case IASTBinaryExpression.op_notequals: + case IASTBinaryExpression.op_greaterEqual: + case IASTBinaryExpression.op_greaterThan: + case IASTBinaryExpression.op_lessEqual: + case IASTBinaryExpression.op_lessThan: + // If both operands are identical pointer types, then they don't need to be + // defined. + if (operand1Type instanceof IPointerType && operand2Type instanceof IPointerType) { + if (isSameType(operand1Type, operand2Type)) { + expression1DefinitionRequired = false; + expression2DefinitionRequired = false; + } + } else if (operand1Type instanceof IPointerType) { + // Only the first operand is a pointer type. It doesn't have to be defined. + expression1DefinitionRequired = false; + } else if (operand2Type instanceof IPointerType) { + // Only the second operand is a pointer type. It doesn't have to be defined. + expression2DefinitionRequired = false; + } + } + + if (expression1DefinitionRequired) { + defineTypeExceptTypedefOrNonFixedEnum(operand1Type); + } + if (expression2DefinitionRequired) { + defineTypeExceptTypedefOrNonFixedEnum(operand2Type); + } + } else if (expression instanceof IASTFunctionCallExpression) { + /* + * The return type and argument types of a function call expression don't need to be + * defined if they're pointer or reference types. The declared and actual types of + * the arguments must further be identical, since implicit type conversions require + * a definition of both the source and the target type. + * + * Example: + * X& foo(X& x); + * void bar(X& x) { + * foo(x); // definition of typeof(foo) and typeof(x) is not required here + * } + * + * Also note that the function call itself doesn't require a definition as long as + * it's not a constructor call: + * + * Example 1: + * void foo() { + * bar(); // definition of bar() is not required here (assuming bar is a function) + * } + * + * Example 2: + * void foo() { + * X(); // definition of X is required here (assuming X is a composite type) + * } + */ + IASTFunctionCallExpression functionCallExpression = (IASTFunctionCallExpression) expression; + IASTExpression functionNameExpression = functionCallExpression.getFunctionNameExpression(); + + if (functionNameExpression instanceof IASTIdExpression) { + IBinding binding = ((IASTIdExpression) functionNameExpression).getName().resolveBinding(); + if (binding instanceof IFunction) { + declareFunction((IFunction) binding, functionCallExpression); + } else if (functionCallExpression instanceof IASTImplicitNameOwner) { + IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) functionCallExpression).getImplicitNames(); + for (IASTName name : implicitNames) { + binding = name.resolveBinding(); + if (binding instanceof IFunction) { + declareFunction((IFunction) binding, functionCallExpression); + } + } + } + } + } else if (expression instanceof IASTFieldReference) { + /* + * The type of the expression part of a field reference always requires a definition. + * + * Example: + * void foo(X& x1, X* x2) { + * x1.bar(); // definition of typeof(x1) is required here + * x2->bar(); // definition of typeof(x2) is required here + * } + */ + + defineTypeExceptTypedefOrNonFixedEnum(((IASTFieldReference) expression).getFieldOwner().getExpressionType()); + } else if (expression instanceof ICPPASTNewExpression) { + /* + * The type specifier of a "new" expression always requires a definition. + * + * Example: + * void foo() { + * new X(); // definition of X is required here + * } + */ + defineTypeExceptTypedefOrNonFixedEnum(((ICPPASTNewExpression) expression).getExpressionType()); + } else if (expression instanceof ICPPASTDeleteExpression) { + /* + * The expression type of a "delete" expression always requires a full definition. + * This is necessary because the compiler needs to be able to call the destructor. + * + * Example: + * void foo(X* x) { + * delete x; // definition of typeof(x) is required here + * } + */ + defineTypeExceptTypedefOrNonFixedEnum(((ICPPASTDeleteExpression) expression).getOperand().getExpressionType()); + } else if (expression instanceof IASTCastExpression) { + /* + * Explicit type casts always need the definition of the underlying types. + * + * Example: + * void foo(X* x) { + * (Y*) x; // definition of both Y and typeof(x) is required here + * } + */ + IASTCastExpression castExpression = (IASTCastExpression) expression; + IType targetType = castExpression.getExpressionType(); + IType sourceType = castExpression.getOperand().getExpressionType(); + + if (!isSameType(targetType, sourceType)) { + // Source and target types of the cast expression are different. + // We need to define both types, even if they're pointers. + defineTypeExceptTypedefOrNonFixedEnum(targetType); + defineTypeExceptTypedefOrNonFixedEnum(sourceType); + } else if (!(targetType instanceof IPointerType) && !(targetType instanceof ICPPReferenceType)) { + // Define the target type if it's not a pointer or reference type. + defineTypeExceptTypedefOrNonFixedEnum(targetType); + } + } + return PROCESS_CONTINUE; + } + + @Override + public int visit(IASTName name) { + // Add the binding associated with the name to the bindings that can be declared + // (we assume that all bindings which have to be defined are already explicitly handled + // elsewhere). + if (name instanceof ICPPASTQualifiedName) { + // All qualifying names must be defined. + IASTName[] names = ((ICPPASTQualifiedName) name).getNames(); + for (int i = 0; i < names.length - 1; i++) { + defineBinding(names[i].resolveBinding()); + } + } + + IBinding binding = name.resolveBinding(); + if (binding != null) { + IBinding owner = binding.getOwner(); + if (owner instanceof IType) { + defineBinding(owner); // Member access requires definition of the containing type. + if (binding instanceof IProblemBinding) + declareBinding(binding); + } else { + declareBinding(binding); // Declare the binding of this name. + } + } + return PROCESS_CONTINUE; + } + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java index abb3e83042a..3b07af209d8 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IHeaderChooser.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IHeaderChooser.java new file mode 100644 index 00000000000..249fe898023 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IHeaderChooser.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2013 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.internal.ui.refactoring.includes; + +import java.util.Collection; + +import org.eclipse.core.runtime.IPath; + +/** + * Interface for selecting one of alternative headers. + */ +public interface IHeaderChooser { + /** + * Chooses one header out of multiple alternatives. + * + * @param bindingName the name of the binding for which the header is selected + * @param headers absolute file system locations of the headers defining the binding + * @return the chosen header or {@code null} if nothing was selected + */ + public IPath chooseHeader(String bindingName, Collection headers); +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java index 801ef05dc89..7e04af30dcf 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java index 6fe6e451e94..13e583b228f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java index 87f92b7f119..2bb0f80ef5c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 @@ -11,8 +11,13 @@ *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.includes; +import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getEndingLineNumber; +import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset; +import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset; +import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getStartingLineNumber; + import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -24,23 +29,29 @@ import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.viewers.LabelProvider; -import org.eclipse.jface.window.Window; -import org.eclipse.osgi.util.NLS; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.dialogs.ElementListSelectionDialog; -import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; import com.ibm.icu.text.Collator; import org.eclipse.cdt.core.dom.IName; +import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.EScopeKind; +import org.eclipse.cdt.core.dom.ast.IASTComment; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorPragmaStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; @@ -62,25 +73,105 @@ import org.eclipse.cdt.core.index.IIndexFileSet; import org.eclipse.cdt.core.index.IIndexInclude; import org.eclipse.cdt.core.index.IIndexName; import org.eclipse.cdt.core.index.IndexLocationFactory; -import org.eclipse.cdt.core.model.IBuffer; import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.parser.Keywords; +import org.eclipse.cdt.core.parser.util.CharArrayIntMap; +import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter; +import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap; +import org.eclipse.cdt.internal.core.parser.scanner.CharArray; +import org.eclipse.cdt.internal.core.parser.scanner.IncludeGuardDetection; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.cdt.internal.core.resources.ResourceLookup; -import org.eclipse.cdt.internal.ui.editor.CEditorMessages; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind; /** * Organizes the include directives and forward declarations of a source or header file. */ public class IncludeOrganizer { + + private static class IncludePrototype { + final IPath header; // null for existing unresolved includes + final IncludeInfo includeInfo; // never null + IASTPreprocessorIncludeStatement existingInclude; // null for newly added includes + final boolean required; // true if the header has to be included + final IncludeGroupStyle style; + + /** Initializes an include prototype for a new include */ + IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) { + this.header = header; + this.includeInfo = includeInfo; + this.style = style; + this.required = true; + } + + /** + * Initializes an include prototype for an existing include. {@code header} may be + * {@code null} if the include was not resolved. + */ + IncludePrototype(IASTPreprocessorIncludeStatement include, IPath header, + IncludeInfo includeInfo, IncludeGroupStyle style) { + this.existingInclude = include; + this.header = header; + this.includeInfo = includeInfo; + this.style = style; + this.required = false; + } + + public void updateFrom(IncludePrototype other) { + this.existingInclude = other.existingInclude; + } + + @Override + public int hashCode() { + if (header != null) + return header.hashCode(); // includeInfo is ignored if header is not null + return includeInfo.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IncludePrototype other = (IncludePrototype) obj; + if (header != null) + return header.equals(other.header); // includeInfo is ignored if header is not null + if (other.header != null) + return false; + if (includeInfo == null) { + if (other.includeInfo != null) + return false; + } + return includeInfo.equals(other.includeInfo); + } + + /** For debugging only */ + @Override + public String toString() { + return header != null ? header.toPortableString() : includeInfo.toString(); + } + } + private static final Collator COLLATOR = Collator.getInstance(); - private static final Comparator PATH_COMPARATOR = new Comparator() { + private static final Comparator INCLUDE_COMPARATOR = new Comparator() { @Override - public int compare(IPath path1, IPath path2) { + public int compare(IncludePrototype include1, IncludePrototype include2) { + IncludeInfo includeInfo1 = include1.includeInfo; + IncludeInfo includeInfo2 = include2.includeInfo; + if (includeInfo1.isSystem() != includeInfo2.isSystem()) { + return includeInfo1.isSystem() ? -1 : 1; + } + IPath path1 = Path.fromOSString(includeInfo1.getName()); + IPath path2 = Path.fromOSString(includeInfo2.getName()); int length1 = path1.segmentCount(); int length2 = path2.segmentCount(); for (int i = 0; i < length1 && i < length2; i++) { @@ -92,16 +183,14 @@ public class IncludeOrganizer { } }; - private final ITextEditor fEditor; + private final IHeaderChooser fHeaderChooser; private final InclusionContext fContext; + private final String fLineDelimiter; - /** - * Constructor - * @param editor The editor on which this organize includes action should operate. - * @param index - */ - public IncludeOrganizer(ITextEditor editor, ITranslationUnit tu, IIndex index) { - fEditor = editor; + public IncludeOrganizer(ITranslationUnit tu, IIndex index, String lineDelimiter, + IHeaderChooser headerChooser) { + fLineDelimiter = lineDelimiter; + fHeaderChooser = headerChooser; fContext = new InclusionContext(tu, index); } @@ -110,10 +199,19 @@ public class IncludeOrganizer { * @param ast The AST translation unit to process. * @throws CoreException */ - public void organizeIncludes(IASTTranslationUnit ast) throws CoreException { + public List organizeIncludes(IASTTranslationUnit ast) throws CoreException { // Process the given translation unit with the inclusion resolver. - BindingClassifier resolver = new BindingClassifier(fContext, ast); - Set bindingsToDefine = resolver.getBindingsToDefine(); + BindingClassifier bindingClassifier = new BindingClassifier(fContext); + bindingClassifier.classifyNodeContents(ast); + Set bindingsToDefine = bindingClassifier.getBindingsToDefine(); + + // Stores the forward declarations for composite types and enumerations as text. + List typeForwardDeclarations = new ArrayList(); + // Stores the forward declarations for C-style functions as text. + List functionForwardDeclarations = new ArrayList(); + + createForwardDeclarations(ast, bindingClassifier, typeForwardDeclarations, functionForwardDeclarations, + bindingsToDefine); HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext); // Create the list of header files which have to be included by examining the list of @@ -123,16 +221,166 @@ public class IncludeOrganizer { List requests = createInclusionRequests(bindingsToDefine, reachableHeaders); processInclusionRequests(requests, headerSubstitutor); - // Stores the forward declarations for composite types and enumerations as text. - List forwardDeclarations = new ArrayList(); + // Use a map instead of a set to be able to retrieve existing elements using equal elements. + // Maps each element to itself. + Map includePrototypes = + new HashMap(); + // Put the new includes into includePrototypes. + for (IPath header : fContext.getHeadersToInclude()) { + IncludeGroupStyle style = getIncludeStyle(header); + IncludeInfo includeInfo = createIncludeInfo(header, style); + IncludePrototype prototype = new IncludePrototype(header, includeInfo, style); + updateIncludePrototypes(includePrototypes, prototype); + } + // Put the existing includes into includePrototypes. + IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives(); + for (IASTPreprocessorIncludeStatement include : existingIncludes) { + if (include.isPartOfTranslationUnitFile()) { + String name = new String(include.getName().getSimpleID()); + IncludeInfo includeInfo = new IncludeInfo(name, include.isSystemInclude()); + String path = include.getPath(); + IPath header = path.isEmpty() ? null : Path.fromPortableString(path); + IncludeGroupStyle style = + header != null ? getIncludeStyle(header) : getIncludeStyle(includeInfo); + IncludePrototype prototype = new IncludePrototype(include, header, includeInfo, style); + updateIncludePrototypes(includePrototypes, prototype); + } + } - // Stores the forward declarations for C-style functions as text. - List functionForwardDeclarations = new ArrayList(); + NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast); + IRegion includeReplacementRegion = getSafeIncludeReplacementRegion(ast, commentedNodeMap); + + IncludePreferences preferences = fContext.getPreferences(); + boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0; - // Create the forward declarations by examining the list of bindings which have to be - // declared. + List edits = new ArrayList(); + + @SuppressWarnings("unchecked") + List[] groupedPrototypes = + (List[]) new List[preferences.includeStyles.size()]; + for (IncludePrototype prototype : includePrototypes.keySet()) { + if (prototype.existingInclude == null + || (allowReordering && isContainedInRegion(prototype.existingInclude, includeReplacementRegion))) { + IncludeGroupStyle groupingStyle = getGroupingStyle(prototype.style); + // If reordering is not allowed, group everything together. + int position = allowReordering ? groupingStyle.getOrder() : 0; + List prototypes = groupedPrototypes[position]; + if (prototypes == null) { + prototypes = new ArrayList(); + groupedPrototypes[position] = prototypes; + } + prototypes.add(prototype); + } + if (!allowReordering && prototype.existingInclude != null + && !prototype.required && prototype.header != null // Unused and resolved. + && isContainedInRegion(prototype.existingInclude, includeReplacementRegion)) { + switch (preferences.unusedStatementsDisposition) { + case REMOVE: + createDelete(prototype.existingInclude, edits); + break; + case COMMENT_OUT: + createCommentOut(prototype.existingInclude, edits); + break; + case KEEP: + break; + } + } + } + + List includeDirectives = new ArrayList(); + IncludeGroupStyle previousParentStyle = null; + for (List prototypes : groupedPrototypes) { + if (prototypes != null && !prototypes.isEmpty()) { + Collections.sort(prototypes, INCLUDE_COMPARATOR); + IncludeGroupStyle style = prototypes.get(0).style; + IncludeGroupStyle groupingStyle = getGroupingStyle(style); + IncludeGroupStyle parentStyle = getParentStyle(groupingStyle); + boolean blankLineBefore = groupingStyle.isBlankLineBefore() || + (parentStyle != null && parentStyle != previousParentStyle && + parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore()); + previousParentStyle = parentStyle; + if (!includeDirectives.isEmpty() && blankLineBefore) + includeDirectives.add(""); // Blank line separator //$NON-NLS-1$ + for (IncludePrototype prototype : prototypes) { + String trailingComment = ""; //$NON-NLS-1$ + IASTPreprocessorIncludeStatement include = prototype.existingInclude; + if (include == null + || (allowReordering && isContainedInRegion(include, includeReplacementRegion))) { + if (include != null) { + List comments = commentedNodeMap.getTrailingCommentsForNode(include); + StringBuilder buf = new StringBuilder(); + for (IASTComment comment : comments) { + buf.append(getPrecedingWhitespace(comment)); + buf.append(comment.getRawSignature()); + } + trailingComment = buf.toString(); + } + String directive = createIncludeDirective(prototype, trailingComment); + if (directive != null) + includeDirectives.add(directive); + } + } + } + } + + // Create the source code to insert into the editor. + + StringBuilder buf = new StringBuilder(); + for (String include : includeDirectives) { + buf.append(include); + buf.append(fLineDelimiter); + } + + if (buf.length() != 0 && !typeForwardDeclarations.isEmpty()) + buf.append(fLineDelimiter); + for (String declaration : typeForwardDeclarations) { + buf.append(declaration); + buf.append(fLineDelimiter); + } + + if (buf.length() != 0 && !functionForwardDeclarations.isEmpty()) + buf.append(fLineDelimiter); + for (String declaration : functionForwardDeclarations) { + buf.append(declaration); + buf.append(fLineDelimiter); + } + + int offset = includeReplacementRegion.getOffset(); + int length = includeReplacementRegion.getLength(); + if (allowReordering) { + if (buf.length() != 0) { + if (offset != 0 && !isPreviousLineBlank(offset)) + buf.insert(0, fLineDelimiter); // Blank line before. + if (!isBlankLineOrEndOfFile(offset + length)) + buf.append(fLineDelimiter); // Blank line after. + } + + String text = buf.toString(); + // TODO(sprigogin): Add a diff algorithm and produce more narrow replacements. + if (!CharArrayUtils.equals(fContext.getTranslationUnit().getContents(), offset, length, text)) { + edits.add(new ReplaceEdit(offset, length, text)); + } + } else if (buf.length() != 0) { + offset += length; + if (!isBlankLineOrEndOfFile(offset)) + buf.append(fLineDelimiter); // Blank line after. + edits.add(new InsertEdit(offset, buf.toString())); + } + + return edits; + } + + /** + * Creates forward declarations by examining the list of bindings which have to be declared. + * Bindings that cannot be safely declared for whatever reason are added to + * {@code bindingsToDefine} set. + */ + private void createForwardDeclarations(IASTTranslationUnit ast, BindingClassifier classifier, + List forwardDeclarations, List functionForwardDeclarations, + Set bindingsToDefine) throws CoreException { + IIndexFileSet reachableHeaders = ast.getIndexFileSet(); Set bindings = - removeBindingsDefinedInIncludedHeaders(resolver.getBindingsToDeclare(), reachableHeaders); + removeBindingsDefinedInIncludedHeaders(classifier.getBindingsToDeclare(), reachableHeaders); for (IBinding binding : bindings) { // Create the text of the forward declaration of this binding. StringBuilder declarationText = new StringBuilder(); @@ -150,6 +398,7 @@ public class IncludeOrganizer { } } catch (DOMException e) { } + Collections.reverse(scopeNames); for (IName scopeName : scopeNames) { declarationText.append("namespace "); //$NON-NLS-1$ @@ -194,15 +443,15 @@ public class IncludeOrganizer { // Append the corresponding keyword. switch (compositeType.getKey()) { - case ICPPClassType.k_class: - declarationText.append("class"); //$NON-NLS-1$ - break; - case ICompositeType.k_struct: - declarationText.append("struct"); //$NON-NLS-1$ - break; - case ICompositeType.k_union: - declarationText.append("union"); //$NON-NLS-1$ - break; + case ICPPClassType.k_class: + declarationText.append("class"); //$NON-NLS-1$ + break; + case ICompositeType.k_struct: + declarationText.append("struct"); //$NON-NLS-1$ + break; + case ICompositeType.k_union: + declarationText.append("union"); //$NON-NLS-1$ + break; } // Append the name of the composite type. @@ -222,8 +471,7 @@ public class IncludeOrganizer { // Append return type and function name. IFunctionType functionType = function.getType(); - // TODO(sprigogin) Improper use of IType.toString(); - declarationText.append(functionType.getReturnType().toString()); + declarationText.append(ASTTypeUtil.getType(functionType.getReturnType(), false)); declarationText.append(' '); declarationText.append(function.getName()); declarationText.append('('); @@ -232,17 +480,16 @@ public class IncludeOrganizer { IType[] parameterTypes = functionType.getParameterTypes(); IParameter[] parameters = function.getParameters(); for (int i = 0; i < parameterTypes.length && i < parameters.length; i++) { - // TODO(sprigogin) Improper use of IType.toString(); - declarationText.append(parameterTypes[i].toString()); + if (i != 0) { + declarationText.append(", "); //$NON-NLS-1$ + } + declarationText.append(ASTTypeUtil.getType(parameterTypes[i], false)); char lastChar = declarationText.charAt(declarationText.length() - 1); if (lastChar != '*' && lastChar != '&') { // Append a space to separate the type name from the parameter name. declarationText.append(' '); } declarationText.append(parameters[i].getName()); - if (i != parameterTypes.length - 1 && i != parameters.length - 1) { - declarationText.append(", "); //$NON-NLS-1$ - } } declarationText.append(");"); //$NON-NLS-1$ @@ -250,7 +497,9 @@ public class IncludeOrganizer { // Add this forward declaration to the separate function forward declaration list. forwardDeclarationListToUse = functionForwardDeclarations; } else { - // We don't handle forward declarations for those types of bindings. Ignore it. + // We can't create a forward declaration for this binding. The binding will have + // to be defined. + bindingsToDefine.add(binding); continue; } @@ -263,79 +512,265 @@ public class IncludeOrganizer { forwardDeclarationListToUse.add(declarationText.toString()); } - Collections.sort(forwardDeclarations); - Collections.sort(functionForwardDeclarations); + Collections.sort(forwardDeclarations, COLLATOR); + Collections.sort(functionForwardDeclarations, COLLATOR); + } - IncludePreferences preferences = fContext.getPreferences(); - Map classifiedHeaders = - new HashMap(fContext.getHeadersToInclude().size()); - for (IPath file : fContext.getHeadersToInclude()) { - classifiedHeaders.put(file, getIncludeStyle(file)); + private void createCommentOut(IASTPreprocessorIncludeStatement include, List edits) { + IASTFileLocation location = include.getFileLocation(); + int offset = location.getNodeOffset(); + if (fContext.getTranslationUnit().isCXXLanguage()) { + offset = getLineStart(offset); + edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$ + } else { + edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$ + int endOffset = offset + location.getNodeLength(); + edits.add(new InsertEdit(endOffset, "*/")); //$NON-NLS-1$ } - @SuppressWarnings("unchecked") - List[] orderedHeaders = (List[]) new List[classifiedHeaders.size()]; - for (Map.Entry entry : classifiedHeaders.entrySet()) { - IPath path = entry.getKey(); - IncludeGroupStyle style = entry.getValue(); - IncludeGroupStyle groupingStyle = getGroupingStyle(style); - int position = groupingStyle.getOrder(); - List headers = orderedHeaders[position]; - if (headers == null) { - headers = new ArrayList(); - orderedHeaders[position] = headers; + } + + private void createDelete(IASTPreprocessorIncludeStatement include, List edits) { + IASTFileLocation location = include.getFileLocation(); + int offset = location.getNodeOffset(); + int endOffset = offset + location.getNodeLength(); + offset = getLineStart(offset); + endOffset = skipToNextLine(endOffset); + edits.add(new DeleteEdit(offset, endOffset - offset)); + } + + private void updateIncludePrototypes(Map includePrototypes, + IncludePrototype prototype) { + IncludePrototype existing = includePrototypes.get(prototype); + if (existing == null) { + includePrototypes.put(prototype, prototype); + } else { + existing.updateFrom(prototype); + } + } + + private boolean isContainedInRegion(IASTNode node, IRegion region) { + return getNodeOffset(node) >= region.getOffset() + && getNodeEndOffset(node) <= region.getOffset() + region.getLength(); + } + + private IRegion getSafeIncludeReplacementRegion(IASTTranslationUnit ast, NodeCommentMap commentMap) { + int maxSafeOffset = ast.getFileLocation().getNodeLength(); + IASTDeclaration[] declarations = ast.getDeclarations(true); + if (declarations.length != 0) + maxSafeOffset = declarations[0].getFileLocation().getNodeOffset(); + + boolean topCommentSkipped = false; + int includeOffset = -1; + int includeEndOffset = -1; + int includeGuardStatementsToSkip = getNumberOfIncludeGuardStatementsToSkip(ast); + int includeGuardEndOffset = -1; + for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) { + if (statement.isPartOfTranslationUnitFile()) { + IASTFileLocation fileLocation = statement.getFileLocation(); + int offset = fileLocation.getNodeOffset(); + if (offset >= maxSafeOffset) + break; + int endOffset = offset + fileLocation.getNodeLength(); + + if (includeGuardStatementsToSkip > 0) { + --includeGuardStatementsToSkip; + includeGuardEndOffset = endOffset; + if (!commentMap.getLeadingCommentsForNode(statement).isEmpty()) { + topCommentSkipped = true; + } + } else if (statement instanceof IASTPreprocessorIncludeStatement) { + if (includeOffset < 0) + includeOffset = offset; + includeEndOffset = endOffset; + includeGuardStatementsToSkip = 0; // Just in case + } else { + break; + } } - headers.add(path); } + if (includeOffset <= 0) { + if (includeGuardEndOffset >= 0) { + includeOffset = skipToNextLine(includeGuardEndOffset); + } else { + includeOffset = 0; + } + if (!topCommentSkipped) { + // Skip the first comment block near the top of the file. + includeOffset = skipStandaloneCommentBlock(includeOffset, maxSafeOffset, ast.getComments(), commentMap); + } + includeEndOffset = includeOffset; + } else { + includeEndOffset = skipToNextLine(includeEndOffset); + } + return new Region(includeOffset, includeEndOffset - includeOffset); + } - List includeDirectives = new ArrayList(); - IncludeGroupStyle previousParentStyle = null; - for (List headers : orderedHeaders) { - if (headers != null && !headers.isEmpty()) { - Collections.sort(headers, PATH_COMPARATOR); - IncludeGroupStyle style = classifiedHeaders.get(headers.get(0)); - IncludeGroupStyle groupingStyle = getGroupingStyle(style); - IncludeGroupStyle parentStyle = getParentStyle(groupingStyle); - boolean blankLineBefore = groupingStyle.isBlankLineBefore() || - (parentStyle != null && parentStyle != previousParentStyle && - parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore()); - previousParentStyle = parentStyle; - if (!includeDirectives.isEmpty() && blankLineBefore) - includeDirectives.add(""); // Blank line separator //$NON-NLS-1$ - for (IPath header : headers) { - style = classifiedHeaders.get(header); - includeDirectives.add(createIncludeDirective(header, style, "")); //$NON-NLS-1$ + private int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) { + IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast); + if (statement == null) + return 0; + + int num = 0; + int offset = 0; + if (isPragmaOnce(statement)) { + num++; + offset = getNodeEndOffset(statement); + } + char[] contents = ast.getRawSignature().toCharArray(); + if (offset != 0) + Arrays.copyOfRange(contents, offset, contents.length); + CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1); + Keywords.addKeywordsPreprocessor(ppKeywords); + if (IncludeGuardDetection.detectIncludeGuard(new CharArray(contents), new LexerOptions(), ppKeywords) != null) { + num += 2; + } + return num; + } + + private IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) { + for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) { + if (statement.isPartOfTranslationUnitFile()) + return statement; + } + return null; + } + private boolean isPragmaOnce(IASTPreprocessorStatement statement) { + if (!(statement instanceof IASTPreprocessorPragmaStatement)) + return false; + return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$ + } + + private int skipToNextLine(int offset) { + char[] contents = fContext.getTranslationUnit().getContents(); + while (offset < contents.length) { + if (contents[offset++] == '\n') + break; + } + return offset; + } + + private int getLineStart(int offset) { + char[] contents = fContext.getTranslationUnit().getContents(); + while (--offset >= 0) { + if (contents[offset] == '\n') + break; + } + return offset + 1; + } + + private int skipToNextLineAfterNode(IASTNode node) { + return skipToNextLine(getNodeEndOffset(node)); + } + + /** + * Returns {@code true} if there are no non-whitespace characters between the given + * {@code offset} and the end of the line. + */ + private boolean isBlankLineOrEndOfFile(int offset) { + char[] contents = fContext.getTranslationUnit().getContents(); + while (offset < contents.length) { + char c = contents[offset++]; + if (c == '\n') + return true; + if (!Character.isWhitespace(c)) + return false; + } + return true; + } + + /** + * Returns {@code true} the line prior to the line corresponding to the given {@code offset} + * does not contain non-whitespace characters. + */ + private boolean isPreviousLineBlank(int offset) { + char[] contents = fContext.getTranslationUnit().getContents(); + while (--offset >= 0) { + if (contents[offset] == '\n') + break; + } + while (--offset >= 0) { + char c = contents[offset]; + if (c == '\n') + return true; + if (!Character.isWhitespace(c)) + return false; + } + return false; + } + + /** + * Returns the whitespace preceding the given node. The newline character in not considered + * whitespace for the purpose of this method. + */ + private String getPrecedingWhitespace(IASTNode node) { + int offset = getNodeOffset(node); + if (offset >= 0) { + char[] contents = fContext.getTranslationUnit().getContents(); + int i = offset; + while (--i >= 0) { + char c = contents[i]; + if (c == '\n' || !Character.isWhitespace(c)) + break; + } + i++; + return new String(contents, i, offset - i); + } + return ""; //$NON-NLS-1$ + } + + private int skipStandaloneCommentBlock(int offset, int endOffset, IASTComment[] comments, NodeCommentMap commentMap) { + Map inverseLeadingMap = new HashMap(); + for (Map.Entry> entry : commentMap.getLeadingMap().entrySet()) { + IASTNode node = entry.getKey(); + if (getNodeOffset(node) <= endOffset) { + for (IASTComment comment : entry.getValue()) { + inverseLeadingMap.put(comment, node); + } + } + } + Map inverseFreestandingMap = new HashMap(); + for (Map.Entry> entry : commentMap.getFreestandingMap().entrySet()) { + IASTNode node = entry.getKey(); + if (getNodeEndOffset(node) < endOffset) { + for (IASTComment comment : entry.getValue()) { + inverseFreestandingMap.put(comment, node); } } } - // Create the source code to insert into the editor. - IBuffer fBuffer = fContext.getTranslationUnit().getBuffer(); - String lineSep = getLineSeparator(fBuffer); - - StringBuilder buf = new StringBuilder(); - for (String include : includeDirectives) { - buf.append(include); - buf.append(lineSep); - } - - if (buf.length() != 0 && !forwardDeclarations.isEmpty()) - buf.append(lineSep); - for (String declaration : forwardDeclarations) { - buf.append(declaration); - buf.append(lineSep); - } - - if (buf.length() != 0 && !functionForwardDeclarations.isEmpty()) - buf.append(lineSep); - for (String declaration : functionForwardDeclarations) { - buf.append(declaration); - buf.append(lineSep); - } - - if (buf.length() != 0) { - buf.append(lineSep); - fBuffer.replace(0, 0, buf.toString()); + for (int i = 0; i < comments.length; i++) { + IASTComment comment = comments[i]; + int commentOffset = getNodeOffset(comment); + if (commentOffset >= offset) { + if (commentOffset >= endOffset) + break; + IASTNode node = inverseLeadingMap.get(comment); + if (node != null) { + List leadingComments = commentMap.getLeadingMap().get(node); + IASTComment previous = leadingComments.get(0); + for (int j = 1; j < leadingComments.size(); j++) { + comment = leadingComments.get(j); + if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1) + return skipToNextLineAfterNode(previous); + previous = comment; + } + if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1) + return skipToNextLineAfterNode(previous); + } + node = inverseFreestandingMap.get(comment); + if (node != null) { + List freestandingComments = commentMap.getFreestandingMap().get(node); + IASTComment previous = freestandingComments.get(0); + for (int j = 1; j < freestandingComments.size(); j++) { + comment = freestandingComments.get(j); + if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1) + return skipToNextLineAfterNode(previous); + previous = comment; + } + } + } } + return offset; } private IncludeGroupStyle getGroupingStyle(IncludeGroupStyle style) { @@ -357,7 +792,7 @@ public class IncludeOrganizer { private IncludeGroupStyle getIncludeStyle(IPath headerPath) { IncludeKind includeKind; IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(headerPath); - if (includeInfo.isSystem()) { + if (includeInfo != null && includeInfo.isSystem()) { if (headerPath.getFileExtension() == null) { includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION; } else { @@ -389,8 +824,24 @@ public class IncludeOrganizer { } } } - Map styles = fContext.getPreferences().includeStyles; - return styles.get(includeKind); + return fContext.getPreferences().includeStyles.get(includeKind); + } + + private IncludeGroupStyle getIncludeStyle(IncludeInfo includeInfo) { + IncludeKind includeKind; + IPath path = Path.fromPortableString(includeInfo.getName()); + if (includeInfo.isSystem()) { + if (path.getFileExtension() == null) { + includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION; + } else { + includeKind = IncludeKind.SYSTEM_WITH_EXTENSION; + } + } else if (isPartnerFile(path)) { + includeKind = IncludeKind.PARTNER; + } else { + includeKind = IncludeKind.EXTERNAL; + } + return fContext.getPreferences().includeStyles.get(includeKind); } private Set removeBindingsDefinedInIncludedHeaders(Set bindings, @@ -426,7 +877,8 @@ public class IncludeOrganizer { private void processInclusionRequests(List requests, HeaderSubstitutor headerSubstitutor) { // Add partner header if necessary. - HashSet partnerIndexFiles = new HashSet(); + HashSet includedByPartner = fContext.getPreferences().allowPartnerIndirectInclusion ? + new HashSet() : null; for (InclusionRequest request : requests) { List candidatePaths = request.getCandidatePaths(); if (candidatePaths.size() == 1) { @@ -434,16 +886,18 @@ public class IncludeOrganizer { if (isPartnerFile(path)) { request.resolve(path); fContext.addHeaderToInclude(path); - try { - IIndexFile indexFile = request.getDeclaringFiles().keySet().iterator().next(); - if (!partnerIndexFiles.contains(indexFile)) { - for (IIndexInclude include : indexFile.getIncludes()) { - fContext.addHeaderAlreadyIncluded(getPath(include.getIncludesLocation())); + if (includedByPartner != null) { + try { + IIndexFile indexFile = request.getDeclaringFiles().keySet().iterator().next(); + if (!includedByPartner.contains(indexFile)) { + for (IIndexInclude include : indexFile.getIncludes()) { + fContext.addHeaderAlreadyIncluded(getPath(include.getIncludesLocation())); + } + includedByPartner.add(indexFile); } - partnerIndexFiles.add(indexFile); + } catch (CoreException e) { + CUIPlugin.log(e); } - } catch (CoreException e) { - CUIPlugin.log(e); } } } @@ -502,8 +956,6 @@ public class IncludeOrganizer { // Resolve ambiguous inclusion requests. // Maps a set of header files presented to the user to the file selected by the user. - HashMap, IPath> userChoiceCache = new HashMap, IPath>(); - for (InclusionRequest request : requests) { if (!request.isResolved()) { List candidatePaths = request.getCandidatePaths(); @@ -513,7 +965,10 @@ public class IncludeOrganizer { break; } } - IPath header = askUserToSelectHeader(request.getBinding(), candidatePaths, userChoiceCache); + IPath header = fHeaderChooser.chooseHeader(request.getBinding().getName(), candidatePaths); + if (header == null) + throw new OperationCanceledException(); + request.resolve(header); if (!fContext.isAlreadyIncluded(header)) fContext.addHeaderToInclude(header); @@ -606,98 +1061,48 @@ public class IncludeOrganizer { return requests; } - /** - * Returns the line separator for the given buffer. - * @param fBuffer - * @return - */ - private String getLineSeparator(IBuffer fBuffer) { - try { - if (fBuffer instanceof IAdaptable) { - IDocument doc= (IDocument) ((IAdaptable) fBuffer).getAdapter(IDocument.class); - if (doc != null) { - String delim= doc.getLineDelimiter(0); - if (delim != null) { - return delim; - } - } - } - } catch (BadLocationException e) { - } - return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ - } - - /** - * Picks a suitable header file that should be used to include the given binding. - * - * @param binding The binding which should be resolved. - * @param headers The available header files to pick from. - * @param userChoiceCache the cache of previous user choices - * @return The chosen header file. - */ - private IPath askUserToSelectHeader(IBinding binding, Collection headers, - HashMap, IPath> userChoiceCache) { - if (headers.isEmpty()) - return null; - if (headers.size() == 1) - return headers.iterator().next(); - - // Check the decision cache. If the cache doesn't help, ask the user. - // Query the cache. - if (userChoiceCache.containsKey(headers)) { - return userChoiceCache.get(headers); - } - - // Ask the user. - final IPath[] elemArray = headers.toArray(new IPath[headers.size()]); - final IPath[] selectedElement = new IPath[1]; - final String bindingName = binding.getName(); - runInUIThread(new Runnable() { - @Override - public void run() { - ElementListSelectionDialog dialog = - new ElementListSelectionDialog(fEditor.getSite().getShell(), new LabelProvider()); - dialog.setElements(elemArray); - dialog.setTitle(CEditorMessages.OrganizeIncludes_label); - dialog.setMessage(NLS.bind(Messages.IncludeOrganizer_ChooseHeader, bindingName)); - if (dialog.open() == Window.OK) { - selectedElement[0] = (IPath) dialog.getFirstResult(); - } - } - }); - - IPath selectedHeader = selectedElement[0]; - - if (selectedHeader == null) - throw new OperationCanceledException(); - - userChoiceCache.put(headers, selectedHeader); // Remember user's choice. - return selectedHeader; - } - - private void runInUIThread(Runnable runnable) { - if (Display.getCurrent() != null) { - runnable.run(); - } else { - Display.getDefault().syncExec(runnable); - } - } - - private String createIncludeDirective(IPath header, IncludeGroupStyle style, String lineComment) { - StringBuilder buf = new StringBuilder("#include "); //$NON-NLS-1$ - buf.append(style.isAngleBrackets() ? '<' : '"'); + private IncludeInfo createIncludeInfo(IPath header, IncludeGroupStyle style) { String name = null; if (style.isRelativePath()) { - IPath relativePath = PathUtil.makeRelativePath(header, fContext.getCurrentDirectory()); - if (relativePath != null) - name = relativePath.toPortableString(); + name = getRelativePath(header); } if (name == null) { IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(header); - name = includeInfo.getName(); + if (includeInfo != null) { + name = includeInfo.getName(); + } else { + name = getRelativePath(header); + } + if (name == null) { + name = header.toPortableString(); // Last resort. + } } - buf.append(name); - buf.append(style.isAngleBrackets() ? '>' : '"'); + return new IncludeInfo(name, style.isAngleBrackets()); + } + + private String getRelativePath(IPath header) { + IPath relativePath = PathUtil.makeRelativePath(header, fContext.getCurrentDirectory()); + if (relativePath == null) + return null; + return relativePath.toPortableString(); + } + + private String createIncludeDirective(IncludePrototype include, String lineComment) { + StringBuilder buf = new StringBuilder(); + // Unresolved includes are preserved out of caution. + if (!include.required && include.header != null) { + switch (fContext.getPreferences().unusedStatementsDisposition) { + case REMOVE: + return null; + case COMMENT_OUT: + buf.append("//"); //$NON-NLS-1$ + break; + case KEEP: + break; + } + } + buf.append("#include "); //$NON-NLS-1$ + buf.append(include.includeInfo.toString()); buf.append(lineComment); return buf.toString(); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java index 3a7fb38653e..3d65250a960 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java @@ -34,26 +34,30 @@ public class IncludePreferences { public final Map includeStyles; public final boolean allowReordering; public final boolean heuristicHeaderSubstitution; + public final boolean allowPartnerIndirectInclusion; public final boolean forwardDeclareCompositeTypes; public final boolean forwardDeclareEnums; public final boolean forwardDeclareFunctions; + // TODO(sprigogin): Create a preference. + public final boolean forwardDeclareExternalVariables = false; + public final boolean forwardDeclareTemplates; public final boolean forwardDeclareNamespaceElements; public final UnusedStatementDisposition unusedStatementsDisposition; public final String[] partnerFileSuffixes; public IncludePreferences(ICProject project) { includeStyles = new HashMap(); - loadStyle(IncludeKind.RELATED, PREF_INCLUDE_STYLE_RELATED, project); - loadStyle(IncludeKind.PARTNER, PREF_INCLUDE_STYLE_PARTNER, project); - loadStyle(IncludeKind.IN_SAME_FOLDER, PREF_INCLUDE_STYLE_SAME_FOLDER, project); - loadStyle(IncludeKind.IN_SUBFOLDER, PREF_INCLUDE_STYLE_SUBFOLDER, project); - loadStyle(IncludeKind.SYSTEM, PREF_INCLUDE_STYLE_SYSTEM, project); - loadStyle(IncludeKind.SYSTEM_WITH_EXTENSION, PREF_INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, project); - loadStyle(IncludeKind.SYSTEM_WITHOUT_EXTENSION, PREF_INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, project); - loadStyle(IncludeKind.OTHER, PREF_INCLUDE_STYLE_OTHER, project); - loadStyle(IncludeKind.IN_SAME_PROJECT, PREF_INCLUDE_STYLE_SAME_PROJECT, project); - loadStyle(IncludeKind.IN_OTHER_PROJECT, PREF_INCLUDE_STYLE_OTHER_PROJECT, project); - loadStyle(IncludeKind.EXTERNAL, PREF_INCLUDE_STYLE_EXTERNAL, project); + loadStyle(IncludeKind.RELATED, PreferenceConstants.INCLUDE_STYLE_RELATED, project); + loadStyle(IncludeKind.PARTNER, PreferenceConstants.INCLUDE_STYLE_PARTNER, project); + loadStyle(IncludeKind.IN_SAME_FOLDER, PreferenceConstants.INCLUDE_STYLE_SAME_FOLDER, project); + loadStyle(IncludeKind.IN_SUBFOLDER, PreferenceConstants.INCLUDE_STYLE_SUBFOLDER, project); + loadStyle(IncludeKind.SYSTEM, PreferenceConstants.INCLUDE_STYLE_SYSTEM, project); + loadStyle(IncludeKind.SYSTEM_WITH_EXTENSION, PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, project); + loadStyle(IncludeKind.SYSTEM_WITHOUT_EXTENSION, PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, project); + loadStyle(IncludeKind.OTHER, PreferenceConstants.INCLUDE_STYLE_OTHER, project); + loadStyle(IncludeKind.IN_SAME_PROJECT, PreferenceConstants.INCLUDE_STYLE_SAME_PROJECT, project); + loadStyle(IncludeKind.IN_OTHER_PROJECT, PreferenceConstants.INCLUDE_STYLE_OTHER_PROJECT, project); + loadStyle(IncludeKind.EXTERNAL, PreferenceConstants.INCLUDE_STYLE_EXTERNAL, project); // Unclassified includes are always kept together. includeStyles.get(IncludeKind.OTHER).setKeepTogether(true); // Normalize order property of the styles to make sure that the numbers are sequential. @@ -65,26 +69,31 @@ public class IncludePreferences { // TODO(sprigogin): Load styles for headers matching patterns. forwardDeclareCompositeTypes = PreferenceConstants.getPreference( - PREF_FORWARD_DECLARE_COMPOSITE_TYPES, project, true); + PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES, project, true); forwardDeclareEnums = PreferenceConstants.getPreference( - PREF_FORWARD_DECLARE_ENUMS, project, false); + PreferenceConstants.FORWARD_DECLARE_ENUMS, project, false); forwardDeclareFunctions = PreferenceConstants.getPreference( - PREF_FORWARD_DECLARE_FUNCTIONS, project, false); + PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, project, false); + forwardDeclareTemplates = PreferenceConstants.getPreference( + PreferenceConstants.FORWARD_DECLARE_TEMPLATES, project, false); forwardDeclareNamespaceElements = PreferenceConstants.getPreference( - PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS, project, true); + PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS, project, true); String value = PreferenceConstants.getPreference( - PREF_PARTNER_FILE_SUFFIXES, project, DEFAULT_PARTNER_FILE_SUFFIXES); + PreferenceConstants.INCLUDES_PARTNER_FILE_SUFFIXES, project, DEFAULT_PARTNER_FILE_SUFFIXES); partnerFileSuffixes = value.split(","); //$NON-NLS-1$ heuristicHeaderSubstitution = PreferenceConstants.getPreference( - PREF_HEURISTIC_HEADER_SUBSTITUTION, project, true); + PreferenceConstants.INCLUDES_HEURISTIC_HEADER_SUBSTITUTION, project, true); allowReordering = PreferenceConstants.getPreference( - PREF_INCLUDES_REORDERING, project, true); + PreferenceConstants.INCLUDES_ALLOW_REORDERING, project, true); + + // TODO(sprigogin): Create a preference for this. + allowPartnerIndirectInclusion = false; // Unused include handling preferences - value = PreferenceConstants.getPreference(PREF_UNUSED_STATEMENTS_DISPOSITION, project, null); + value = PreferenceConstants.getPreference(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION, project, null); UnusedStatementDisposition disposition = null; if (value != null) disposition = UnusedStatementDisposition.valueOf(value); @@ -103,148 +112,6 @@ public class IncludePreferences { includeStyles.put(includeKind, style); } - // TODO(sprigogin): Move the constants and defaults to PreferenceConstants. - - /** - * Whether composite types should be forward declared if possible. - * - * Examples: - * class X; - * struct Y; - * union Z; - */ - public static final String PREF_FORWARD_DECLARE_COMPOSITE_TYPES = "forward_declare_composite_types"; //$NON-NLS-1$ - - /** - * Whether C++11-style enums should be forward declared if possible. - * - * Example: - * enum class X; - */ - public static final String PREF_FORWARD_DECLARE_ENUMS = "forward_declare_enums"; //$NON-NLS-1$ - - /** - * Whether C-style functions should be forward declared if possible. - * - * Example: - * void foo(); - */ - public static final String PREF_FORWARD_DECLARE_FUNCTIONS = "forward_declare_functions"; //$NON-NLS-1$ - - /** - * Whether elements nested within namespaces should be forward declared if possible. - * - * Examples: - * namespace N { class X; } - */ - public static final String PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS = "forward_declare_namespace_elements"; //$NON-NLS-1$ - - /** - * Defines a list of file name suffixes. A header file and the including file are considered - * partners if their file names without extensions are either identical or differ by one of - * these suffixes. - */ - public static final String PREF_PARTNER_FILE_SUFFIXES = "include_partner_file_suffixes"; //$NON-NLS-1$ - - /** - * Whether a heuristic approach should be used to decide which C++ header files to include. - * The heuristic prefers headers which have no file extension and / or are named like the symbol - * which should be defined. This often works out nicely since it's a commonly used naming - * convention for C++ library headers. - */ - public static final String PREF_HEURISTIC_HEADER_SUBSTITUTION = "organize_includes_heuristic_header_substitution"; //$NON-NLS-1$ - - /** - * Whether it's allowed to reorder existing include directives. If this preference is set to - * false, the original order is kept as far as possible. This may be necessary to avoid breaking - * code which makes assumptions about the order of the include directives. If this is set to - * true, a different sort order can be applied. Groups of includes are ordered according to - * the values returned by - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle#getOrder()} method. - * Includes within each group are ordered alphabetically. - */ - public static final String PREF_INCLUDES_REORDERING = "organize_includes_allow_reordering"; //$NON-NLS-1$ - - /** - * Determines what should be done with any unused include directives and forward declarations. - * This preference may have one of the three values defined by - * {@link UnusedStatementDisposition} enumeration ("REMOVE", "COMMENT_OUT", "KEEP"). - */ - public static final String PREF_UNUSED_STATEMENTS_DISPOSITION = "organize_includes_unused_statements"; //$NON-NLS-1$ - - /** - * Include style for headers closely related to the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_RELATED = "include_style_related"; //$NON-NLS-1$ - /** - * Include style for the header with the same name as the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_PARTNER = "include_style_partner"; //$NON-NLS-1$ - /** - * Include style for headers in the same folder as the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SAME_FOLDER = "include_style_same_folder"; //$NON-NLS-1$ - /** - * Include style for headers in subfolders of the folder containing the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SUBFOLDER = "include_style_subfolder"; //$NON-NLS-1$ - /** - * Include style for system headers. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SYSTEM = "include_style_system"; //$NON-NLS-1$ - /** - * Include style for C-style system headers with a file name extension. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SYSTEM_WITH_EXTENSION = "include_style_system_with_extension"; //$NON-NLS-1$ - /** - * Include style for C++-style system headers without a file name extension. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION = "include_style_system_without_extension"; //$NON-NLS-1$ - /** - * Include style for headers not closely related to the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_OTHER = "include_style_other"; //$NON-NLS-1$ - /** - * Include style for headers in the same project as the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_SAME_PROJECT = "include_style_in_same_project"; //$NON-NLS-1$ - /** - * Include style for headers in a different project than the including file. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_OTHER_PROJECT = "include_style_in_other_project"; //$NON-NLS-1$ - /** - * Include style for headers outside Eclipse workspace. - * The value of the preference is an XML representation of - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. - */ - public static final String PREF_INCLUDE_STYLE_EXTERNAL = "include_style_external"; //$NON-NLS-1$ - /** - * Include styles for headers matching user-defined patterns. - * The value of the preference is an XML representation of one or more - * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}s. - */ - public static final String PREF_INCLUDE_STYLE_MATCHING_PATTERN = "include_style_matching_pattern"; //$NON-NLS-1$ - /** * Initializes the given preference store with the default values. * @@ -252,50 +119,51 @@ public class IncludePreferences { */ public static void initializeDefaultValues(IPreferenceStore store) { IncludeGroupStyle style = new IncludeGroupStyle(IncludeKind.RELATED); - store.setDefault(PREF_INCLUDE_STYLE_RELATED, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_RELATED, style.toString()); style = new IncludeGroupStyle(IncludeKind.PARTNER); style.setKeepTogether(true); style.setBlankLineBefore(true); style.setOrder(0); - store.setDefault(PREF_INCLUDE_STYLE_PARTNER, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_PARTNER, style.toString()); style = new IncludeGroupStyle(IncludeKind.IN_SAME_FOLDER); - store.setDefault(PREF_INCLUDE_STYLE_SAME_FOLDER, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_FOLDER, style.toString()); style = new IncludeGroupStyle(IncludeKind.IN_SUBFOLDER); - store.setDefault(PREF_INCLUDE_STYLE_SUBFOLDER, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SUBFOLDER, style.toString()); style = new IncludeGroupStyle(IncludeKind.SYSTEM); style.setKeepTogether(true); style.setBlankLineBefore(true); - store.setDefault(PREF_INCLUDE_STYLE_SYSTEM, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM, style.toString()); style = new IncludeGroupStyle(IncludeKind.SYSTEM_WITH_EXTENSION); style.setKeepTogether(true); style.setAngleBrackets(true); style.setOrder(1); - store.setDefault(PREF_INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITH_EXTENSION, style.toString()); style = new IncludeGroupStyle(IncludeKind.SYSTEM_WITHOUT_EXTENSION); style.setKeepTogether(true); style.setAngleBrackets(true); style.setOrder(2); - store.setDefault(PREF_INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION, style.toString()); style = new IncludeGroupStyle(IncludeKind.OTHER); style.setKeepTogether(true); style.setBlankLineBefore(true); style.setOrder(3); - store.setDefault(PREF_INCLUDE_STYLE_OTHER, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER, style.toString()); style = new IncludeGroupStyle(IncludeKind.IN_SAME_PROJECT); - store.setDefault(PREF_INCLUDE_STYLE_SAME_PROJECT, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_SAME_PROJECT, style.toString()); style = new IncludeGroupStyle(IncludeKind.IN_OTHER_PROJECT); - store.setDefault(PREF_INCLUDE_STYLE_OTHER_PROJECT, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_OTHER_PROJECT, style.toString()); style = new IncludeGroupStyle(IncludeKind.EXTERNAL); - store.setDefault(PREF_INCLUDE_STYLE_EXTERNAL, style.toString()); - store.setDefault(PREF_INCLUDE_STYLE_MATCHING_PATTERN, ""); //$NON-NLS-1$ + store.setDefault(PreferenceConstants.INCLUDE_STYLE_EXTERNAL, style.toString()); + store.setDefault(PreferenceConstants.INCLUDE_STYLE_MATCHING_PATTERN, ""); //$NON-NLS-1$ - store.setDefault(PREF_PARTNER_FILE_SUFFIXES, DEFAULT_PARTNER_FILE_SUFFIXES); - store.setDefault(PREF_HEURISTIC_HEADER_SUBSTITUTION, true); - store.setDefault(PREF_INCLUDES_REORDERING, true); - store.setDefault(PREF_FORWARD_DECLARE_COMPOSITE_TYPES, true); - store.setDefault(PREF_FORWARD_DECLARE_ENUMS, false); - store.setDefault(PREF_FORWARD_DECLARE_FUNCTIONS, false); - store.setDefault(PREF_FORWARD_DECLARE_NAMESPACE_ELEMENTS, true); - store.setDefault(PREF_UNUSED_STATEMENTS_DISPOSITION, UnusedStatementDisposition.COMMENT_OUT.toString()); + store.setDefault(PreferenceConstants.INCLUDES_PARTNER_FILE_SUFFIXES, DEFAULT_PARTNER_FILE_SUFFIXES); + store.setDefault(PreferenceConstants.INCLUDES_HEURISTIC_HEADER_SUBSTITUTION, true); + store.setDefault(PreferenceConstants.INCLUDES_ALLOW_REORDERING, true); + store.setDefault(PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES, true); + store.setDefault(PreferenceConstants.FORWARD_DECLARE_ENUMS, false); + store.setDefault(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, false); + store.setDefault(PreferenceConstants.FORWARD_DECLARE_TEMPLATES, false); + store.setDefault(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS, true); + store.setDefault(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION, UnusedStatementDisposition.COMMENT_OUT.toString()); } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java index 14110018a5a..1f090cc552a 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java index a5a88e92b2a..0345cb3b0d7 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 @@ -34,7 +34,7 @@ import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility; * Context for managing include statements. */ public class InclusionContext { - private static final IPath UNRESOLVED_INCLUDE = new Path(""); //$NON-NLS-1$ + private static final IPath UNRESOLVED_INCLUDE = Path.EMPTY; private final ITranslationUnit fTu; private final IProject fProject; @@ -132,22 +132,19 @@ public class InclusionContext { String headerLocation = fullPath.toOSString(); String shortestInclude = null; boolean isSystem = false; - int count = 0; //XXX for (IncludeSearchPathElement pathElement : fIncludeSearchPath.getElements()) { String includeDirective = pathElement.getIncludeDirective(headerLocation); if (includeDirective != null && (shortestInclude == null || shortestInclude.length() > includeDirective.length())) { shortestInclude = includeDirective; isSystem = !pathElement.isForQuoteIncludesOnly(); - if (count < 1) //XXX - isSystem = false; //XXX Hack to introduce non-system includes } - count++; //XXX } if (shortestInclude == null) return null; include = new IncludeInfo(shortestInclude, isSystem); - fIncludeResolutionCache.put(include, fullPath); + // Don't put an include to fullPath to fIncludeResolutionCache since it may be wrong + // if the header was included by #include_next. fInverseIncludeResolutionCache.put(fullPath, include); return include; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java index 65202dba345..72a5e66bffd 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 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 diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties deleted file mode 100644 index b4b60369ab9..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/Messages.properties +++ /dev/null @@ -1,11 +0,0 @@ -############################################################################### -# Copyright (c) 2012 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 -############################################################################### -IncludeOrganizer_ChooseHeader=Choose a header file to include for symbol ''{0}'' diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java index 200467ba02c..86c3fac94f5 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java @@ -1953,6 +1953,197 @@ public class PreferenceConstants { */ public static final String FUNCTION_PASS_OUTPUT_PARAMETERS_BY_POINTER = "function_pass_output_parameters_by_pointer"; //$NON-NLS-1$ + /** + * Whether composite types should be forward declared if possible. + * + * Examples: + * class X; + * struct Y; + * union Z; + * + * @since 5.6 + */ + public static final String FORWARD_DECLARE_COMPOSITE_TYPES = "forwardDeclare.compositeTypes"; //$NON-NLS-1$ + + /** + * Whether C++11-style enums should be forward declared if possible. + * + * Example: + * enum class X; + * + * @since 5.6 + */ + public static final String FORWARD_DECLARE_ENUMS = "forwardDeclare.enums"; //$NON-NLS-1$ + + /** + * Whether C-style functions should be forward declared if possible. + * + * Example: + * void foo(); + * + * @since 5.6 + */ + public static final String FORWARD_DECLARE_FUNCTIONS = "forwardDeclare.functions"; //$NON-NLS-1$ + + /** + * Whether C++ templates should be forward declared if possible. + * + * Examples: + * template class foo; + * template void bar(T p); + * + * @since 5.6 + */ + public static final String FORWARD_DECLARE_TEMPLATES = "forwardDeclare.templates"; //$NON-NLS-1$ + + /** + * Whether elements nested within namespaces should be forward declared if possible. + * + * Examples: + * namespace N { class X; } + * + * @since 5.6 + */ + public static final String FORWARD_DECLARE_NAMESPACE_ELEMENTS = "forwardDeclare.namespaceElements"; //$NON-NLS-1$ + + /** + * Defines a list of file name suffixes. A header file and the including file are considered + * partners if their file names without extensions are either identical or differ by one of + * these suffixes. + * + * @since 5.6 + */ + public static final String INCLUDES_PARTNER_FILE_SUFFIXES = "includes.partnerFileSuffixes"; //$NON-NLS-1$ + + /** + * Whether a heuristic approach should be used to decide which C++ header files to include. + * The heuristic prefers headers which have no file extension and / or are named like the symbol + * which should be defined. This often works out nicely since it's a commonly used naming + * convention for C++ library headers. + * + * @since 5.6 + */ + public static final String INCLUDES_HEURISTIC_HEADER_SUBSTITUTION = "organizeIncludes.heuristicHeader_substitution"; //$NON-NLS-1$ + + /** + * Whether it's allowed to reorder existing include directives. If this preference is set to + * false, the original order is kept as far as possible. This may be necessary to avoid breaking + * code which makes assumptions about the order of the include directives. If this is set to + * true, a different sort order can be applied. Groups of includes are ordered according to + * the values returned by + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle#getOrder()} method. + * Includes within each group are ordered alphabetically. + * + * @since 5.6 + */ + public static final String INCLUDES_ALLOW_REORDERING = "organizeIncludes.allowReordering"; //$NON-NLS-1$ + + /** + * Determines what should be done with any unused include directives and forward declarations. + * This preference may have one of the three values defined by + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences.UnusedStatementDisposition} + * enumeration ("REMOVE", "COMMENT_OUT", "KEEP"). + * + * @since 5.6 + */ + public static final String INCLUDES_UNUSED_STATEMENTS_DISPOSITION = "organizeIncludes.unusedStatements"; //$NON-NLS-1$ + + /** + * Include style for headers closely related to the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_RELATED = "includeStyle.related"; //$NON-NLS-1$ + /** + * Include style for the header with the same name as the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_PARTNER = "includeStyle.partner"; //$NON-NLS-1$ + /** + * Include style for headers in the same folder as the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SAME_FOLDER = "includeStyle.sameFolder"; //$NON-NLS-1$ + /** + * Include style for headers in subfolders of the folder containing the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SUBFOLDER = "includeStyle.subfolder"; //$NON-NLS-1$ + /** + * Include style for system headers. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SYSTEM = "includeStyle.system"; //$NON-NLS-1$ + /** + * Include style for C-style system headers with a file name extension. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SYSTEM_WITH_EXTENSION = "includeStyle.systemWithExtension"; //$NON-NLS-1$ + /** + * Include style for C++-style system headers without a file name extension. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SYSTEM_WITHOUT_EXTENSION = "includeStyle.systemWithoutExtension"; //$NON-NLS-1$ + /** + * Include style for headers not closely related to the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_OTHER = "includeStyle.other"; //$NON-NLS-1$ + /** + * Include style for headers in the same project as the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_SAME_PROJECT = "includeStyle.inSameProject"; //$NON-NLS-1$ + /** + * Include style for headers in a different project than the including file. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_OTHER_PROJECT = "includeStyle.inOtherProject"; //$NON-NLS-1$ + /** + * Include style for headers outside Eclipse workspace. + * The value of the preference is an XML representation of + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_EXTERNAL = "includeStyle.external"; //$NON-NLS-1$ + /** + * Include styles for headers matching user-defined patterns. + * The value of the preference is an XML representation of one or more + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle}s. + * + * @since 5.6 + */ + public static final String INCLUDE_STYLE_MATCHING_PATTERN = "includeStyle.matchingPattern"; //$NON-NLS-1$ /** * Returns the CDT-UI preference store. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java index 3bc790af8ae..3ab152fcfa9 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java @@ -139,6 +139,13 @@ public class CdtActionConstants { */ public static final String ADD_INCLUDE= "org.eclipse.cdt.ui.actions.AddInclude"; //$NON-NLS-1$ + /** + * Source menu: name of standard Organize Includes global action + * (value "org.eclipse.cdt.ui.actions.OrganizeIncludes"). + * @since 5.6 + */ + public static final String ORGANIZE_INCLUDES= "org.eclipse.cdt.ui.actions.OrganizeIncludes"; //$NON-NLS-1$ + /** * Source menu: name of standard Sort Lines global action * (value "org.eclipse.cdt.ui.actions.SortLines"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java index 366b373cf51..16333a03b4b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java @@ -160,7 +160,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange editor.setAction("AddIncludeOnSelection", fAddInclude); //$NON-NLS-1$ fOrganizeIncludes= new OrganizeIncludesAction(editor); - // TODO: fOrganizeIncludes.setActionDefinitionId(ICEditorActionDefinitionIds.ORGANIZE_INCLUDES); + fOrganizeIncludes.setActionDefinitionId(ICEditorActionDefinitionIds.ORGANIZE_INCLUDES); editor.setAction("OrganizeIncludes", fOrganizeIncludes); //$NON-NLS-1$ // fSortMembers= new SortMembersAction(editor);