diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/ArrayUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/ArrayUtil.java index e1963735b1d..ed518c28ec3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/ArrayUtil.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/ArrayUtil.java @@ -349,17 +349,18 @@ public abstract class ArrayUtil { * Adds all elements of an array to a collection. * @since 5.4 */ - public static void addAll(Collection collection, T[] array) { + @SafeVarargs + public static void addAll(Collection collection, T... elements) { if (collection instanceof ArrayList) { - ((ArrayList) collection).ensureCapacity(collection.size() + array.length); + ((ArrayList) collection).ensureCapacity(collection.size() + elements.length); } - for (T element : array) { + for (T element : elements) { collection.add(element); } } /** - * Returns whether the specified array contains the specified object. Comparison is by + * Returns whether the specified array contains the specified object. The comparison is by * object identity. * * @param array the array to search 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 2396e128d2a..9cfbd14f0f6 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 @@ -92,7 +92,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { private void assertDeclared(String... names) { classifyBindings(); - assertExpectedBindings(names, fBindingClassifier.getBindingsToDeclare(), "declared"); + assertExpectedBindings(names, fBindingClassifier.getBindingsToForwardDeclare(), "declared"); } private void assertExpectedBindings(String[] expectedNames, Set bindings, String verb) { @@ -176,7 +176,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // c->m(); // } public void testClassHierarchy() throws Exception { - assertDefined("b", "B", "c", "C"); + assertDefined("b", "B", "c", "C", "m"); assertDeclared(); } @@ -186,7 +186,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // a->m(); // } public void testMethodCall() throws Exception { - assertDefined("A"); + assertDefined("A", "m"); assertDeclared(); } @@ -300,7 +300,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true); // A header declaring the function is not responsible for defining the parameter type since // the implicit conversion from B to A is provided externally to parameter type. - assertDefined("B"); + assertDefined("B", "A"); assertDeclared(); } @@ -337,6 +337,17 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { assertDeclared("A"); } + // class A { + // void m(); + // }; + + // void A::m() { + // } + public void testMethodDefinition() throws Exception { + assertDefined("A"); + assertDeclared(); + } + // struct A { // A(const char* s); // }; @@ -384,7 +395,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // a(1); // } public void testCallOperator() throws Exception { - assertDefined("A", "a"); + assertDefined("A", "a", "operator ()"); assertDeclared(); } @@ -497,7 +508,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // b->m(); // } public void testTemplatesAllowingIncompleteParameterType_2() throws Exception { - assertDefined("B", "b"); + assertDefined("B", "b", "m"); assertDeclared(); } @@ -520,7 +531,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // c->x->m(); // } public void testTemplatesAllowingIncompleteParameterType_3() throws Exception { - assertDefined("B", "C"); + assertDefined("B", "C", "m"); assertDeclared(); } @@ -541,7 +552,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // f()->m(); // } public void testTemplatesAllowingIncompleteParameterType_4() throws Exception { - assertDefined("B", "f"); + assertDefined("B", "f", "m"); assertDeclared(); } @@ -564,7 +575,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { // c->f()->m(); // } public void testTemplatesAllowingIncompleteParameterType_5() throws Exception { - assertDefined("B", "C"); + assertDefined("B", "C", "f", "m"); assertDeclared(); } 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 8e43052550d..98a517cddff 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 @@ -473,6 +473,37 @@ public class IncludeOrganizerTest extends IncludesTestBase { assertExpectedResults(); } + //h1.h + //struct A { + // template + // void m(const T& p); + //}; + + //h2.h + //#include "h1.h" + //template + //void A::m(const T& p) { + //} + + //h3.h + //#include "h1.h" + //typedef A B; + + //source.cpp + //void test(B& b) { + // b.m(1); + //} + //==================== + //#include "h2.h" + //#include "h3.h" + // + //void test(B& b) { + // b.m(1); + //} + public void testMethodDefinedInHeader() throws Exception { + assertExpectedResults(); + } + //h1.h //namespace ns3 { //class C {}; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java index 600e794dea4..6e110c3944d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java @@ -69,7 +69,7 @@ public class InclusionContext { return fCurrentDirectory; } - public IncludePreferences getPreferences() { + public final IncludePreferences getPreferences() { return fPreferences; } 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 9079ae2e8db..a4072d5f840 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 @@ -18,9 +18,11 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.HashSet; import java.util.Set; @@ -127,26 +129,28 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; * must be defined and a set of bindings that must be declared. */ public class BindingClassifier { + public enum InclusionType { DECLARATION, DEFINITION } + private final IncludeCreationContext fContext; 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; + private final Set fBindingsToForwardDeclare; /** The AST that the classifier is working on. */ private IASTTranslationUnit fAst; private final BindingCollector fBindingCollector; private final Set fProcessedDefinedBindings; private final Set fProcessedDeclaredBindings; - private static final Set templatesAllowingIncompleteArgumentType = - Collections.unmodifiableSet(new HashSet(Arrays.asList(new String[] { - "enable_shared_from_this", // 20.7.2.4 //$NON-NLS-1$ - "declval", // 20.2.4 //$NON-NLS-1$ - "default_delete", // 20.7.1.1 //$NON-NLS-1$ - "shared_ptr", // 20.7.2.2 //$NON-NLS-1$ - "unique_ptr", // 20.7.1 //$NON-NLS-1$ - "weak_ptr" // 20.7.2.3 //$NON-NLS-1$ - }))); + private static final String[] TEMPLATES_ALLOWING_INCOMPLETE_ARGUMENT_TYPE = { + // Please keep alphabetical order. + "enable_shared_from_this", // 20.7.2.4 //$NON-NLS-1$ + "declval", // 20.2.4 //$NON-NLS-1$ + "default_delete", // 20.7.1.1 //$NON-NLS-1$ + "shared_ptr", // 20.7.2.2 //$NON-NLS-1$ + "unique_ptr", // 20.7.1 //$NON-NLS-1$ + "weak_ptr" // 20.7.2.3 //$NON-NLS-1$ + }; /** * @param context the context for binding classification @@ -154,10 +158,10 @@ public class BindingClassifier { public BindingClassifier(IncludeCreationContext context) { fContext = context; fPreferences = context.getPreferences(); - fBindingsToDefine = new HashSet(); - fBindingsToDeclare = new HashSet(); - fProcessedDefinedBindings = new HashSet(); - fProcessedDeclaredBindings = new HashSet(); + fBindingsToDefine = new HashSet<>(); + fBindingsToForwardDeclare = new HashSet<>(); + fProcessedDefinedBindings = new HashSet<>(); + fProcessedDeclaredBindings = new HashSet<>(); fBindingCollector = new BindingCollector(); } @@ -178,8 +182,8 @@ public class BindingClassifier { /** * Returns the bindings which only require a simple forward declaration. */ - public Set getBindingsToDeclare() { - return fBindingsToDeclare; + public Set getBindingsToForwardDeclare() { + return fBindingsToForwardDeclare; } /** @@ -306,46 +310,56 @@ public class BindingClassifier { * or an empty list if no such binding is available. */ private Set getRequiredBindings(IBinding binding) { - Set bindings = new HashSet(); + if (binding instanceof ICPPNamespace) + return Collections.emptySet(); - if (binding instanceof ICPPMember) { - // If the binding is a member, get its owning composite type. - binding = binding.getOwner(); - } else if (binding instanceof IVariable) { + Deque queue = new ArrayDeque<>(); + + addRequiredBindings(binding, queue); + + Set bindings = new HashSet<>(); + + while ((binding = queue.poll()) != null) { + if (!bindings.add(binding)) + continue; if (binding instanceof ICPPSpecialization) { - bindings.add(((ICPPSpecialization) binding).getSpecializedBinding()); - } else { - bindings.add(binding); - } - } else if (binding instanceof IType) { - // Resolve the type. - binding = getTypeBinding((IType) binding); - } else if (binding instanceof ICPPNamespace) { - // Namespaces are neither declared nor defined. - binding = null; - } - - if (binding instanceof ICPPSpecialization) { - ICPPTemplateParameterMap parameterMap = ((ICPPSpecialization) binding).getTemplateParameterMap(); - for (Integer position : parameterMap.getAllParameterPositions()) { - ICPPTemplateArgument argument = parameterMap.getArgument(position); - if (argument != null) { - IType type = argument.getTypeValue(); - // Normally we don't need to define parameters of a template specialization that - // were not specified explicitly. __gnu_cxx::hash is an exception from that rule. - if (type instanceof IBinding && "hash".equals(((IBinding) type).getName())) { //$NON-NLS-1$ - IBinding owner = ((IBinding) type).getOwner(); - if (owner instanceof ICPPNamespace && "__gnu_cxx".equals(owner.getName())) //$NON-NLS-1$ - bindings.add((IBinding) type); + ICPPTemplateParameterMap parameterMap = ((ICPPSpecialization) binding).getTemplateParameterMap(); + for (Integer position : parameterMap.getAllParameterPositions()) { + ICPPTemplateArgument argument = parameterMap.getArgument(position); + if (argument != null) { + IType type = argument.getTypeValue(); + // Normally we don't need to define parameters of a template specialization + // that were not specified explicitly. __gnu_cxx::hash is an exception from + // that rule. + if (type instanceof IBinding && "hash".equals(((IBinding) type).getName())) { //$NON-NLS-1$ + IBinding owner = ((IBinding) type).getOwner(); + if (owner instanceof ICPPNamespace && "__gnu_cxx".equals(owner.getName())) //$NON-NLS-1$ + addRequiredBindings((IBinding) type, queue); + } } } + // Get the specialized binding - e.g. get the binding for X if the current binding is + // for the template specialization X. + addRequiredBindings(((ICPPSpecialization) binding).getSpecializedBinding(), queue); } - // Get the specialized binding - e.g. get the binding for X if the current binding is - // for the template specialization X. - binding = ((ICPPSpecialization) binding).getSpecializedBinding(); } - if (binding instanceof IProblemBinding) { + return bindings; + } + + private void addRequiredBindings(IBinding binding, Deque newBindings) { + if (binding instanceof ICPPMember) { + if (binding instanceof ICPPMethod) { + newBindings.add(binding); // Include the method in case we need its definition. + } + // If the binding is a member, get its owning composite type. + newBindings.add(binding.getOwner()); + } else if (binding instanceof IType) { + // Remove type qualifiers. + IBinding b = getTypeBinding((IType) binding); + if (b != null) + newBindings.add(b); + } else if (binding instanceof IProblemBinding) { IProblemBinding problemBinding = (IProblemBinding) binding; IBinding[] candidateBindings = problemBinding.getCandidateBindings(); @@ -354,25 +368,19 @@ public class BindingClassifier { // different candidates are very often defined within the same target file anyway, // so it won't affect the list of generated include directives. This therefore // allows us to be a little more fault tolerant here. - for (IBinding candidateBinding : candidateBindings) { - bindings.add(candidateBinding); - } + Collections.addAll(newBindings, candidateBindings); } else { // No candidate bindings available. Check whether this is a macro. try { - IIndexMacro[] indexMacros = fContext.getIndex().findMacros(binding.getNameCharArray(), - IndexFilter.ALL, null); - for (IIndexMacro indexMacro : indexMacros) { - bindings.add(indexMacro); - } + IIndexMacro[] indexMacros = + fContext.getIndex().findMacros(binding.getNameCharArray(), IndexFilter.ALL, null); + Collections.addAll(newBindings, indexMacros); } catch (CoreException e) { } } - } else if (binding != null) { - bindings.add(binding); + } else { + newBindings.add(binding); } - - return bindings; } private void declareType(IType type) { @@ -404,7 +412,8 @@ public class BindingClassifier { Collection requiredBindings = getRequiredBindings(binding); for (IBinding requiredBinding : requiredBindings) { - if (fBindingsToDeclare.contains(requiredBinding) || fBindingsToDefine.contains(requiredBinding)) { + if (fBindingsToForwardDeclare.contains(requiredBinding) || + fBindingsToDefine.contains(requiredBinding)) { return; } if (fAst.getDefinitionsInAST(requiredBinding).length != 0) { @@ -416,7 +425,7 @@ public class BindingClassifier { if (canForwardDeclare(requiredBinding)) { if (requiredBinding == binding) { - fBindingsToDeclare.add(requiredBinding); + fBindingsToForwardDeclare.add(requiredBinding); } else { declareBinding(requiredBinding); } @@ -493,18 +502,19 @@ public class BindingClassifier { private void defineBinding(IBinding binding) { if (!markAsDefined(binding)) return; - if (fAst.getDefinitionsInAST(binding).length != 0) return; // Defined locally. Collection requiredBindings = getRequiredBindings(binding); for (IBinding requiredBinding : requiredBindings) { - fBindingsToDeclare.remove(requiredBinding); - if (requiredBinding == binding) { - fBindingsToDefine.add(requiredBinding); - } else { - defineBinding(requiredBinding); + fBindingsToForwardDeclare.remove(requiredBinding); + if (requiredBinding != binding) { + if (!markAsDefined(requiredBinding)) + continue; + if (fAst.getDefinitionsInAST(requiredBinding).length != 0) + continue; // Defined locally. } + fBindingsToDefine.add(requiredBinding); } } @@ -538,7 +548,7 @@ public class BindingClassifier { for (ICPPClassType base : bases) { fProcessedDefinedBindings.add(base); fBindingsToDefine.remove(base); - fBindingsToDeclare.remove(base); + fBindingsToForwardDeclare.remove(base); } } @@ -567,7 +577,7 @@ public class BindingClassifier { if (type instanceof ICPPTemplateInstance) { ICPPTemplateInstance instance = (ICPPTemplateInstance) type; IBinding template = instance.getSpecializedBinding(); - if (templatesAllowingIncompleteArgumentType.contains(template.getName())) { + if (isTemplateAllowingIncompleteArgumentType(template)) { ICPPTemplateArgument[] arguments = instance.getTemplateArguments(); if (arguments.length != 0) { IType argumentType = arguments[0].getTypeValue(); @@ -589,6 +599,17 @@ public class BindingClassifier { return false; } + private static boolean isTemplateAllowingIncompleteArgumentType(IBinding binding) { + String name = binding.getName(); + int pos = Arrays.binarySearch(TEMPLATES_ALLOWING_INCOMPLETE_ARGUMENT_TYPE, name); + if (pos < 0) + return false; + IBinding owner = binding.getOwner(); + if (!(owner instanceof ICPPNamespace)) + return false; + return CharArrayUtils.equals(owner.getNameCharArray(), STD) && owner.getOwner() == null; + } + private class BindingCollector extends ASTVisitor { BindingCollector() { super(true); @@ -1297,8 +1318,7 @@ public class BindingClassifier { !CharArrayUtils.equals(owner.getNameCharArray(), STD) || owner.getOwner() != null) { return true; } - String templateName = template.getName(); - if (!templatesAllowingIncompleteArgumentType.contains(templateName)) + if (!isTemplateAllowingIncompleteArgumentType(template)) return true; // For most templates allowing incomplete argument type a full definition of the argument 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 cb8738e268e..5b895abbd05 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 @@ -54,7 +54,7 @@ public class IncludeCreationContext extends InclusionContext { return fSourceContents; } - public IIndex getIndex() { + public final IIndex getIndex() { return fIndex; } 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 85d6601d6cc..9efbd106b3d 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 @@ -78,6 +78,7 @@ import org.eclipse.cdt.core.index.IIndexInclude; import org.eclipse.cdt.core.index.IIndexName; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.parser.Keywords; +import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.core.parser.util.CharArrayIntMap; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.ui.CUIPlugin; @@ -199,7 +200,7 @@ public class IncludeOrganizer { // Process the given translation unit with the inclusion resolver. BindingClassifier bindingClassifier = new BindingClassifier(fContext); bindingClassifier.classifyNodeContents(ast); - Set bindingsToDefine = bindingClassifier.getBindingsToDefine(); + Set bindingsToInclude = bindingClassifier.getBindingsToDefine(); IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives(); fContext.addHeadersIncludedPreviously(existingIncludes); @@ -209,7 +210,7 @@ public class IncludeOrganizer { // bindings which have to be defined. IIndexFileSet reachableHeaders = ast.getIndexFileSet(); - List requests = createInclusionRequests(ast, bindingsToDefine, false, reachableHeaders); + List requests = createInclusionRequests(ast, bindingsToInclude, false, reachableHeaders); processInclusionRequests(requests, headerSubstitutor); // Use a map instead of a set to be able to retrieve existing elements using equal elements. @@ -357,7 +358,7 @@ public class IncludeOrganizer { IIndexFileSet reachableHeaders = ast.getIndexFileSet(); Set bindings = - removeBindingsDefinedInIncludedHeaders(ast, classifier.getBindingsToDeclare(), reachableHeaders); + removeBindingsDefinedInIncludedHeaders(ast, classifier.getBindingsToForwardDeclare(), reachableHeaders); for (IBinding binding : bindings) { // Create the text of the forward declaration of this binding. StringBuilder declarationText = new StringBuilder(); @@ -748,13 +749,12 @@ public class IncludeOrganizer { private Set removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast, Set bindings, IIndexFileSet reachableHeaders) throws CoreException { - Set filteredBindings = new HashSet<>(bindings); - List requests = createInclusionRequests(ast, bindings, true, reachableHeaders); Set allIncludedHeaders = new HashSet<>(); allIncludedHeaders.addAll(fContext.getHeadersAlreadyIncluded()); allIncludedHeaders.addAll(fContext.getHeadersToInclude()); + Set filteredBindings = new HashSet<>(bindings); for (InclusionRequest request : requests) { if (isSatisfiedByIncludedHeaders(request, allIncludedHeaders)) filteredBindings.remove(request.getBinding()); @@ -992,12 +992,12 @@ public class IncludeOrganizer { } private List createInclusionRequests(IASTTranslationUnit ast, - Set bindingsToDefine, boolean allowDeclarations, + Set bindingsToInclude, boolean allowDeclarations, IIndexFileSet reachableHeaders) throws CoreException { - List requests = new ArrayList(bindingsToDefine.size()); + List requests = new ArrayList(bindingsToInclude.size()); IIndex index = fContext.getIndex(); - binding_loop: for (IBinding binding : bindingsToDefine) { + binding_loop: for (IBinding binding : bindingsToInclude) { IIndexName[] indexNames; if (binding instanceof IMacroBinding) { indexNames = IIndexName.EMPTY_ARRAY; @@ -1012,12 +1012,30 @@ public class IncludeOrganizer { } } } - } else if (allowDeclarations || binding instanceof IFunction || binding instanceof IVariable) { - // For functions and variables we need to include a declaration. + } else if (allowDeclarations || binding instanceof IVariable) { + // For a variable we need to include a declaration. indexNames = index.findDeclarations(binding); + } else if (binding instanceof ICPPMethod) { + // Include the headers containing method definitions except the ones also containing + // the definition of the owner class. The headers defining the owner class are taken + // care of separately. + Set declarationFiles = new HashSet<>(); + IIndexName[] declarations = index.findNames(binding, IIndex.FIND_DECLARATIONS); + for (IIndexName declaration : declarations) { + IIndexFile file = declaration.getFile(); + if (file != null) { + declarationFiles.add(file); + } + } + IIndexName[] definitions = index.findDefinitions(binding); + indexNames = filterIncludableNotInBlacklistedFiles(definitions, declarationFiles); } else { - // For all other bindings we need to include the definition. indexNames = index.findDefinitions(binding); + if (binding instanceof IFunction) { + // If a function is defined in a header, include that header. + // Otherwise look for declarations. + indexNames = filterIncludableNotInBlacklistedFiles(indexNames, Collections.emptySet()); + } if (indexNames.length == 0) { // If we could not find any definitions, there is still a chance that // a declaration would be sufficient. @@ -1035,12 +1053,11 @@ public class IncludeOrganizer { } } - Map declaringHeaders = new HashMap(); - Map reachableDeclaringHeaders = new HashMap(); + Map declaringHeaders = new HashMap<>(); + Map reachableDeclaringHeaders = new HashMap<>(); for (IIndexName indexName : indexNames) { IIndexFile indexFile = indexName.getFile(); - if (IncludeUtil.isSource(indexFile, fContext.getProject()) && - index.findIncludedBy(indexFile, 0).length == 0) { + if (!canBeIncluded(indexFile)) { // The target is a source file which isn't included by any other files. // Don't include it. continue; @@ -1064,6 +1081,23 @@ public class IncludeOrganizer { return requests; } + private IIndexName[] filterIncludableNotInBlacklistedFiles(IIndexName[] names, Set blacklist) + throws CoreException { + IIndexName[] includable = IIndexName.EMPTY_ARRAY; + int pos = 0; + for (IIndexName name : names) { + IIndexFile file = name.getFile(); + if (file != null && !blacklist.contains(file) && canBeIncluded(file)) + includable = ArrayUtil.appendAt(includable, pos++, name); + } + return ArrayUtil.trim(includable, pos); + } + + private boolean canBeIncluded(IIndexFile indexFile) throws CoreException { + return !IncludeUtil.isSource(indexFile, fContext.getProject()) || + fContext.getIndex().findIncludedBy(indexFile, 0).length != 0; + } + private String createIncludeDirective(IncludePrototype include, String lineComment) { StringBuilder buf = new StringBuilder(); // Unresolved includes are preserved out of caution. Partner include is always preserved.