diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java index 016a537f009..e3086ee9136 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java @@ -24,7 +24,9 @@ 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.HeaderSubstitutionMap; import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeMap; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences.UnusedStatementDisposition; @@ -58,6 +60,7 @@ public class IncludeOrganizerTest extends IncludesTestBase { preferenceStore.setToDefault(PreferenceConstants.FORWARD_DECLARE_TEMPLATES); preferenceStore.setToDefault(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS); preferenceStore.setToDefault(PreferenceConstants.INCLUDES_ALLOW_REORDERING); + preferenceStore.setToDefault(IncludePreferences.INCLUDES_HEADER_SUBSTITUTION); preferenceStore.setToDefault(IncludePreferences.INCLUDES_SYMBOL_EXPORTING_HEADERS); } @@ -338,6 +341,33 @@ public class IncludeOrganizerTest extends IncludesTestBase { assertExpectedResults(); } + //h1.h + //class A {}; + + //h2.h + //#include "h1.h" // IWYU pragma: export + //class B {}; + + //h3.h + //#include "h2.h" + + //source.cpp + //A a; + //B b; + //==================== + //#include "h3.h" + // + //A a; + //B b; + public void testIndirectHeaderExport() throws Exception { + HeaderSubstitutionMap headerMap = new HeaderSubstitutionMap("Test", false, + new IncludeMap(true, new String[] { "h2.h", "h3.h"}), + new IncludeMap(false)); + getPreferenceStore().setValue(IncludePreferences.INCLUDES_HEADER_SUBSTITUTION, + HeaderSubstitutionMap.serializeMaps(Collections.singletonList(headerMap))); + assertExpectedResults(); + } + //h1.h //#define M2(t, p) t p diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java index 24a7bde4e39..7660d460d96 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java @@ -59,7 +59,7 @@ public class IncludeCreationContext extends InclusionContext { } /** - * Removes headers that are exported by other headers that will be included + * Removes headers that are exported by other headers that will be included. */ public void removeExportedHeaders() throws CoreException { // Index files keyed by their absolute paths. @@ -69,8 +69,14 @@ public class IncludeCreationContext extends InclusionContext { filesByPath.put(path, file); } - Set exportedHeaders = new HashSet(); - for (IPath path : fHeadersToInclude) { + removeExportedHeaders(fHeadersAlreadyIncluded, filesByPath); + removeExportedHeaders(fHeadersToInclude, filesByPath); + } + + private void removeExportedHeaders(Set exportingHeaders, + Map filesByPath) throws CoreException { + Set exportedHeaders = new HashSet(); + for (IPath path : exportingHeaders) { if (!exportedHeaders.contains(path)) { IIndexFile file = filesByPath.get(path); if (file != null) { // file can be null if the header was not indexed. @@ -91,7 +97,7 @@ public class IncludeCreationContext extends InclusionContext { } } fHeadersToInclude.removeAll(exportedHeaders); - } + } private static IPath getPath(IIndexFile file) throws CoreException { return IndexLocationFactory.getAbsolutePath(file.getLocation()); @@ -103,6 +109,7 @@ public class IncludeCreationContext extends InclusionContext { public final void addHeaderToInclude(IPath header) { fHeadersToInclude.add(header); + fHeadersAlreadyIncluded.add(header); } public final boolean isToBeIncluded(IPath header) { 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 2441ddc457c..8105552b2b8 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 @@ -746,6 +746,7 @@ public class IncludeOrganizer { if (!request.isResolved() && !isExportedBinding(request, headerSubstitutor)) { List candidatePaths = request.getCandidatePaths(); Set representativeHeaders = new HashSet(); + Set representedHeaders = new HashSet(); boolean allRepresented = true; for (IPath path : candidatePaths) { if (fContext.isIncluded(path)) { @@ -759,6 +760,7 @@ public class IncludeOrganizer { IPath header = headerSubstitutor.getUniqueRepresentativeHeader(path); if (header != null) { representativeHeaders.add(header); + representedHeaders.add(path); } else { allRepresented = false; } @@ -772,6 +774,10 @@ public class IncludeOrganizer { System.out.println(request.toString() + " (unique representative)"); //$NON-NLS-1$ if (!fContext.isAlreadyIncluded(path)) fContext.addHeaderToInclude(path); + for (IPath header : representedHeaders) { + if (!header.equals(path)) + fContext.addHeaderAlreadyIncluded(header); + } } } } @@ -799,6 +805,8 @@ public class IncludeOrganizer { } if (!fContext.isAlreadyIncluded(header)) fContext.addHeaderToInclude(header); + if (!header.equals(path)) + fContext.addHeaderAlreadyIncluded(path); } } }