mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-13 03:55:22 +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");
|
||||
}
|
||||
|
||||
// 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 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.IASTNode;
|
||||
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.IASTPreprocessorMacroExpansion;
|
||||
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.IEnumeration;
|
||||
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.IPointerType;
|
||||
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.ICPPMethod;
|
||||
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.ICPPSpecialization;
|
||||
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.cpp.CPPASTIdExpression;
|
||||
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.semantics.CPPSemantics;
|
||||
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}.
|
||||
*/
|
||||
private boolean hasConvertingConstructor(ICPPClassType classType, IASTInitializerClause argument) {
|
||||
|
@ -235,6 +237,30 @@ public class BindingClassifier {
|
|||
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
|
||||
* 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) {
|
||||
for (IASTDeclarator declarator : declarators) {
|
||||
if (!(declarator instanceof IASTFunctionDeclarator) &&
|
||||
declarator.getPointerOperators().equals(IASTPointerOperator.EMPTY_ARRAY)) {
|
||||
declarator.getPointerOperators().length == 0) {
|
||||
canBeDeclared = false;
|
||||
break;
|
||||
}
|
||||
|
@ -531,7 +557,13 @@ public class BindingClassifier {
|
|||
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
|
||||
* a pointer or reference type.
|
||||
|
@ -542,24 +574,59 @@ public class BindingClassifier {
|
|||
* Example 2:
|
||||
* 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) {
|
||||
IFunction function = (IFunction) binding;
|
||||
|
||||
// Define the return type if necessary
|
||||
IType returnType = function.getType().getReturnType();
|
||||
if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) {
|
||||
defineTypeExceptTypedefOrNonFixedEnum(returnType);
|
||||
}
|
||||
IFunctionType functionType = function.getType();
|
||||
if (declarator.getPropertyInParent() == IASTFunctionDefinition.DECLARATOR) {
|
||||
// Define the return type if necessary.
|
||||
IType returnType = functionType.getReturnType();
|
||||
if (!(returnType instanceof IPointerType) && !(returnType instanceof ICPPReferenceType)) {
|
||||
defineTypeExceptTypedefOrNonFixedEnum(returnType);
|
||||
}
|
||||
|
||||
// Define parameter types if necessary
|
||||
IType[] parameterTypes = function.getType().getParameterTypes();
|
||||
for (IType type : parameterTypes) {
|
||||
if (!(type instanceof IPointerType) && !(type instanceof ICPPReferenceType)) {
|
||||
defineTypeExceptTypedefOrNonFixedEnum(type);
|
||||
// Define parameter types if necessary.
|
||||
IType[] parameterTypes = functionType.getParameterTypes();
|
||||
for (IType type : parameterTypes) {
|
||||
if (!(type instanceof IPointerType)) {
|
||||
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;
|
||||
}
|
||||
|
@ -679,22 +746,6 @@ public class BindingClassifier {
|
|||
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
|
||||
public int visit(IASTStatement statement) {
|
||||
if (statement instanceof IASTReturnStatement) {
|
||||
|
|
Loading…
Add table
Reference in a new issue