mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-13 12:05:21 +02:00
Bug 415394 - Function parameter types allowing implicit conversion from
other types should not be forward-declared
This commit is contained in:
parent
c9a53b072d
commit
b7d7630bda
2 changed files with 98 additions and 32 deletions
|
@ -291,6 +291,21 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
assertDeclared("A");
|
assertDeclared("A");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// struct A {
|
||||||
|
// A(const char* s);
|
||||||
|
// };
|
||||||
|
// struct B {
|
||||||
|
// explicit B(const char* s);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// void f(A a, B b);
|
||||||
|
public void testFunctionDeclarationWithTypeConversion() throws Exception {
|
||||||
|
// A file declaring the function is responsible for defining the parameter type that
|
||||||
|
// provides constructor that can be used for implicit conversion.
|
||||||
|
assertDefined("A");
|
||||||
|
assertDeclared("B");
|
||||||
|
}
|
||||||
|
|
||||||
// struct A {};
|
// struct A {};
|
||||||
// struct B {};
|
// struct B {};
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
|
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
|
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
|
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
|
||||||
|
@ -68,6 +67,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||||
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IEnumeration;
|
import org.eclipse.cdt.core.dom.ast.IEnumeration;
|
||||||
import org.eclipse.cdt.core.dom.ast.IFunction;
|
import org.eclipse.cdt.core.dom.ast.IFunction;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IFunctionType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IParameter;
|
import org.eclipse.cdt.core.dom.ast.IParameter;
|
||||||
import org.eclipse.cdt.core.dom.ast.IPointerType;
|
import org.eclipse.cdt.core.dom.ast.IPointerType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
|
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
|
||||||
|
@ -87,6 +87,7 @@ 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.ICPPMember;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
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.ICPPNamespace;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
|
||||||
|
@ -98,6 +99,7 @@ import org.eclipse.cdt.core.index.IndexFilter;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
|
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
|
||||||
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions;
|
||||||
|
@ -211,7 +213,7 @@ public class BindingClassifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if {@code targetType} has a constructor that can be used for
|
* Returns {@code true} if the {@code classType} has a constructor that can be used for
|
||||||
* implicit conversion from {@code argument}.
|
* implicit conversion from {@code argument}.
|
||||||
*/
|
*/
|
||||||
private boolean hasConvertingConstructor(ICPPClassType classType, IASTInitializerClause argument) {
|
private boolean hasConvertingConstructor(ICPPClassType classType, IASTInitializerClause argument) {
|
||||||
|
@ -235,6 +237,30 @@ public class BindingClassifier {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@code classType} has a constructor that can be used for
|
||||||
|
* implicit conversion from some other type.
|
||||||
|
*/
|
||||||
|
private boolean hasConvertingConstructor(ICPPClassType classType, IASTNode point) {
|
||||||
|
ICPPConstructor[] constructors = ClassTypeHelper.getConstructors(classType, point);
|
||||||
|
for (ICPPConstructor constructor : constructors) {
|
||||||
|
if (!constructor.isExplicit()) {
|
||||||
|
ICPPParameter[] parameters = constructor.getParameters();
|
||||||
|
if (parameters.length != 0 && CPPFunction.getRequiredArgumentCount(parameters) <= 1) {
|
||||||
|
IType type = getNestedType(parameters[0].getType(), REF | ALLCVQ);
|
||||||
|
if (!classType.isSameType(type))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTypeWithConvertingConstructor(IType type, IASTNode point) {
|
||||||
|
type = getNestedType(type, REF | ALLCVQ);
|
||||||
|
return type instanceof ICPPClassType && hasConvertingConstructor((ICPPClassType) type, point);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the given binding and returns the binding(s) which we actually have to either
|
* 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
|
* declare or define. As an example, if the given binding is a variable, this function returns
|
||||||
|
@ -519,7 +545,7 @@ public class BindingClassifier {
|
||||||
if (!staticMember) {
|
if (!staticMember) {
|
||||||
for (IASTDeclarator declarator : declarators) {
|
for (IASTDeclarator declarator : declarators) {
|
||||||
if (!(declarator instanceof IASTFunctionDeclarator) &&
|
if (!(declarator instanceof IASTFunctionDeclarator) &&
|
||||||
declarator.getPointerOperators().equals(IASTPointerOperator.EMPTY_ARRAY)) {
|
declarator.getPointerOperators().length == 0) {
|
||||||
canBeDeclared = false;
|
canBeDeclared = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -531,7 +557,13 @@ public class BindingClassifier {
|
||||||
defineBindingForName(name);
|
defineBindingForName(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (declaration instanceof IASTFunctionDefinition) {
|
}
|
||||||
|
return PROCESS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int visit(IASTDeclarator declarator) {
|
||||||
|
if (declarator instanceof IASTFunctionDeclarator) {
|
||||||
/*
|
/*
|
||||||
* The type specifier of a function definition doesn't need to be defined if it is
|
* The type specifier of a function definition doesn't need to be defined if it is
|
||||||
* a pointer or reference type.
|
* a pointer or reference type.
|
||||||
|
@ -542,24 +574,59 @@ public class BindingClassifier {
|
||||||
* Example 2:
|
* Example 2:
|
||||||
* X& foo() { } // definition of X is not required here
|
* X& foo() { } // definition of X is not required here
|
||||||
*/
|
*/
|
||||||
IBinding binding = ((IASTFunctionDefinition) declaration).getDeclarator().getName().resolveBinding();
|
IBinding binding = declarator.getName().resolveBinding();
|
||||||
if (binding instanceof IFunction) {
|
if (binding instanceof IFunction) {
|
||||||
IFunction function = (IFunction) binding;
|
IFunction function = (IFunction) binding;
|
||||||
|
|
||||||
// Define the return type if necessary
|
IFunctionType functionType = function.getType();
|
||||||
IType returnType = function.getType().getReturnType();
|
if (declarator.getPropertyInParent() == IASTFunctionDefinition.DECLARATOR) {
|
||||||
if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) {
|
// Define the return type if necessary.
|
||||||
defineTypeExceptTypedefOrNonFixedEnum(returnType);
|
IType returnType = functionType.getReturnType();
|
||||||
}
|
if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) {
|
||||||
|
defineTypeExceptTypedefOrNonFixedEnum(returnType);
|
||||||
|
}
|
||||||
|
|
||||||
// Define parameter types if necessary
|
// Define parameter types if necessary.
|
||||||
IType[] parameterTypes = function.getType().getParameterTypes();
|
IType[] parameterTypes = functionType.getParameterTypes();
|
||||||
for (IType type : parameterTypes) {
|
for (IType type : parameterTypes) {
|
||||||
if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) {
|
if (!(type instanceof IPointerType)) {
|
||||||
defineTypeExceptTypedefOrNonFixedEnum(type);
|
if (!(type instanceof ICPPReferenceType) ||
|
||||||
|
isTypeWithConvertingConstructor(type, declarator)) {
|
||||||
|
defineTypeExceptTypedefOrNonFixedEnum(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// As a matter of policy, a function declaration is responsible for
|
||||||
|
// providing definitions of parameter types that have implicit converting
|
||||||
|
// constructors.
|
||||||
|
IType[] parameterTypes = functionType.getParameterTypes();
|
||||||
|
for (IType type : parameterTypes) {
|
||||||
|
if (!(type instanceof IPointerType)) {
|
||||||
|
if (isTypeWithConvertingConstructor(type, declarator)) {
|
||||||
|
defineTypeExceptTypedefOrNonFixedEnum(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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;
|
return PROCESS_CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -679,22 +746,6 @@ public class BindingClassifier {
|
||||||
return PROCESS_CONTINUE;
|
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
|
@Override
|
||||||
public int visit(IASTStatement statement) {
|
public int visit(IASTStatement statement) {
|
||||||
if (statement instanceof IASTReturnStatement) {
|
if (statement instanceof IASTReturnStatement) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue