mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 14:42:11 +02:00
Bug 45203. Produce include staements for macros. Don't generate include
statements for types brought in by macro expansion.
This commit is contained in:
parent
89ba6685cb
commit
b603ca080d
5 changed files with 149 additions and 61 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue