diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/util/OneSourceMultipleHeadersTestCase.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/util/OneSourceMultipleHeadersTestCase.java index eb681dc47e3..f8f3e821e13 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/util/OneSourceMultipleHeadersTestCase.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/util/OneSourceMultipleHeadersTestCase.java @@ -68,12 +68,16 @@ public class OneSourceMultipleHeadersTestCase extends BaseTestCase { return ast; } - public String getAstSource() { + protected String getAstSource() { return testData[testData.length - 1].toString(); } @Override protected void setUp() throws Exception { + setUp(false); + } + + protected void setUp(boolean generateIncludeStatements) throws Exception { cproject = cpp ? CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) : CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER); @@ -87,6 +91,15 @@ public class OneSourceMultipleHeadersTestCase extends BaseTestCase { } } + if (generateIncludeStatements) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < getTestData().length - 1; i++) { + String filename = String.format("header%d.h", i + 1); + buf.append(String.format("#include \"header%d.h\"\n", i + 1)); + } + testData[testData.length - 1].insert(0, buf); + } + IFile cppfile= TestSourceReader.createFile(cproject.getProject(), new Path("source.c" + (cpp ? "pp" : "")), getAstSource()); waitForIndexer(cproject); 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 b6b9281b02c..2c092ccc2bd 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 @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Semaphore; @@ -149,7 +150,18 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat protected final IASTName[] getMacroDefinitionsInAST(IMacroBinding binding) { if (fLocationResolver == null) return IASTName.EMPTY_NAME_ARRAY; - return fLocationResolver.getDeclarations(binding); + + IASTName[] declarations = fLocationResolver.getDeclarations(binding); + int j = 0; + for (int i = 0; i < declarations.length; i++) { + IASTName name = declarations[i]; + if (name.isPartOfTranslationUnitFile()) { + declarations[j++] = name; + } + } + if (j < declarations.length) + return j > 0 ? Arrays.copyOf(declarations, j) : IASTName.EMPTY_NAME_ARRAY; + return declarations; } protected final IASTName[] getMacroReferencesInAST(IMacroBinding binding) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxFile.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxFile.java index d9c82e5f5b9..d8b65f096fb 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxFile.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxFile.java @@ -104,10 +104,11 @@ class LocationCtxFile extends LocationCtxContainer { } public boolean isThisFile(int sequenceNumber) { + if (sequenceNumber < 0) + return false; LocationCtx child= findChildLessOrEqualThan(sequenceNumber, false); - if (!(child instanceof LocationCtxFile)) { + if (!(child instanceof LocationCtxFile)) return true; - } return sequenceNumber >= child.fSequenceNumber + child.getSequenceLength(); } 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 9d5d48a1c48..2c6f6849aff 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 @@ -51,7 +51,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { @Override protected void setUp() throws Exception { - super.setUp(); + super.setUp(true); IASTTranslationUnit ast = getAst(); fIndex = CCorePlugin.getIndexManager().getIndex(getCProject(), IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); @@ -223,4 +223,16 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { assertDefined("D"); assertDeclared(); } + + // struct A {}; + // struct B {}; + // struct C {}; + // struct prefixD {}; + // #define MACRO(t1, v1, t2, v3, t4, v4) t1 v1; t2 b; C v3; prefix##t4 v4 + + // MACRO(A, a, B, c, D, d); + public void testMacro() throws Exception { + assertDefined("A", "B", "MACRO"); + assertDeclared(); + } } 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 ccf5112b159..cba5e2d688f 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 @@ -44,14 +44,18 @@ 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.IASTImageLocation; 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.IASTMacroExpansionLocation; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStatement; @@ -186,28 +190,6 @@ public class BindingClassifier { } } - /** - * Returns whether the two given types are identical. This does the same as IType.isSameType() - * with the exception that it considers a pointer and the zero literal identical. - */ - private boolean isSameType(IType type1, IType type2) { - if (type1 == null || type2 == null) { - return false; - } - if (type1.isSameType(type2)) { - return true; - } - - if (type1 instanceof IPointerType || type2 instanceof IPointerType) { - if ((type1 instanceof IBasicType && ((IBasicType) type1).getKind() == Kind.eInt) - || (type2 instanceof IBasicType && ((IBasicType) type2).getKind() == Kind.eInt)) { - return true; - } - } - - return false; - } - /** * Resolves the given binding and returns the binding(s) which we actually have to either * declare or define. As an example, if the given binding is a variable, this function returns @@ -277,24 +259,6 @@ public class BindingClassifier { return bindings; } - /** - * Resolves the given type to a binding which we actually have to either declare or define. - * As an example if the given type is a pointer type, this function returns the binding for - * the raw (i.e. nested) type of the pointer. This is because we actually have to declare or - * define the raw type of a pointer, not the pointer type itself. - * - * @param type The type to resolve. - * @return A binding which is suitable for either declaration or definition, or {@code null} - * if no such binding is available. - */ - private IBinding getTypeBinding(IType type) { - type = getNestedType(type, ALLCVQ | PTR | ARRAY | REF); - if (type instanceof IBinding) { - return (IBinding) type; - } - return null; - } - /** * Adds the given binding to the list of bindings which have to be forward declared. * @@ -380,29 +344,15 @@ public class BindingClassifier { } } - 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)) + if (!markAsDefined(binding)) return; - if (binding instanceof ITypedef) { - IType type = ((ITypedef) binding).getType(); - type = SemanticUtil.getNestedType(type, ALLCVQ); - if (type instanceof IBinding) { - // Record the fact that we also have a definition of the typedef's target type. - fProcessedDefinedBindings.add((IBinding) type); - } - } - if (fAst.getDefinitionsInAST(binding).length != 0) { return; // Defined locally } @@ -414,6 +364,29 @@ public class BindingClassifier { } } + /** + * Marks the given binding as defined. + * + * @param binding the binding to mark + * @return {{@code true} if the binding has not yet been marked as defined, + * {@code false} otherwise. + */ + private boolean markAsDefined(IBinding binding) { + if (!fProcessedDefinedBindings.add(binding)) + return false; + + if (binding instanceof ITypedef) { + IType type = ((ITypedef) binding).getType(); + type = SemanticUtil.getNestedType(type, ALLCVQ); + if (type instanceof IBinding) { + // Record the fact that we also have a definition of the typedef's target type. + fProcessedDefinedBindings.add((IBinding) type); + } + } + + return true; + } + private void declareFunction(IFunction function, IASTFunctionCallExpression functionCallExpression) { // Handle return or expression type of the function or constructor call. IType returnType = function.getType().getReturnType(); @@ -483,7 +456,13 @@ public class BindingClassifier { } if (!canBeDeclared) { - defineBinding(((IASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding()); + IASTName name = ((IASTNamedTypeSpecifier) declSpecifier).getName(); + IBinding binding = name.resolveBinding(); + if (isPartOfExternalMacroDefinition(name)) { + markAsDefined(binding); + } else { + defineBinding(binding); + } } } } else if (declaration instanceof IASTFunctionDefinition) { @@ -1016,5 +995,76 @@ public class BindingClassifier { } return PROCESS_CONTINUE; } + + @Override + public int visit(IASTTranslationUnit tu) { + for (IASTPreprocessorMacroExpansion macroExpansion : tu.getMacroExpansions()) { + IBinding binding = macroExpansion.getMacroReference().getBinding(); + defineBinding(binding); + } + return PROCESS_CONTINUE; + } + } + + /** + * Returns whether the two given types are identical. This does the same as IType.isSameType() + * with the exception that it considers a pointer and the zero literal identical. + */ + private static boolean isSameType(IType type1, IType type2) { + if (type1 == null || type2 == null) { + return false; + } + if (type1.isSameType(type2)) { + return true; + } + + if (type1 instanceof IPointerType || type2 instanceof IPointerType) { + if ((type1 instanceof IBasicType && ((IBasicType) type1).getKind() == Kind.eInt) + || (type2 instanceof IBasicType && ((IBasicType) type2).getKind() == Kind.eInt)) { + return true; + } + } + + return false; + } + + /** + * Resolves the given type to a binding which we actually have to either declare or define. + * As an example if the given type is a pointer type, this function returns the binding for + * the raw (i.e. nested) type of the pointer. This is because we actually have to declare or + * define the raw type of a pointer, not the pointer type itself. + * + * @param type The type to resolve. + * @return A binding which is suitable for either declaration or definition, or {@code null} + * if no such binding is available. + */ + private static IBinding getTypeBinding(IType type) { + type = getNestedType(type, ALLCVQ | PTR | ARRAY | REF); + if (type instanceof IBinding) { + return (IBinding) type; + } + return null; + } + + private static boolean isEnumerationWithoutFixedUnderlyingType(IBinding typeBinding) { + return typeBinding instanceof IEnumeration + && (!(typeBinding instanceof ICPPEnumeration) || ((ICPPEnumeration) typeBinding).getFixedType() == null); + } + + private static boolean isPartOfExternalMacroDefinition(IASTName name) { + IASTNodeLocation[] locations = name.getNodeLocations(); + if (locations.length != 1 || !(locations[0] instanceof IASTMacroExpansionLocation)) + return false; + + IASTMacroExpansionLocation macroExpansionLocation = (IASTMacroExpansionLocation) locations[0]; + IASTPreprocessorMacroExpansion macroExpansion = macroExpansionLocation.getExpansion(); + if (macroExpansion.getMacroDefinition().isPartOfTranslationUnitFile()) + return false; + IASTImageLocation imageLocation = name.getImageLocation(); + if (imageLocation != null && + imageLocation.getFileName().equals(name.getTranslationUnit().getFilePath())) { + return false; + } + return true; } } \ No newline at end of file