/******************************************************************************* * Copyright (c) 2012, 2016 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Sergey Prigogin (Google) - initial API and implementation * Mathias Kunter *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.includes; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor.STD; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ALLCVQ; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ARRAY; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.PTR; 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; import org.eclipse.core.runtime.CoreException; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTDoStatement; import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTFieldReference; import org.eclipse.cdt.core.dom.ast.IASTForStatement; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; 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.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; 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; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; import org.eclipse.cdt.core.dom.ast.IBasicType; 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.IMacroBinding; import org.eclipse.cdt.core.dom.ast.IParameter; import org.eclipse.cdt.core.dom.ast.IPointerType; import org.eclipse.cdt.core.dom.ast.IProblemBinding; import org.eclipse.cdt.core.dom.ast.IQualifierType; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.ITypedef; import org.eclipse.cdt.core.dom.ast.IVariable; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerList; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; 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.ICPPNamespaceAlias; 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; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap; import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexMacro; import org.eclipse.cdt.core.index.IIndexName; import org.eclipse.cdt.core.index.IndexFilter; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; 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.CPPClosureType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper.MethodKind; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.LookupData; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; /** * For a whole translation unit or a part of it determines a set of externally defined bindings that * must be defined and a set of bindings that must be declared. */ public class BindingClassifier { 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$ }; private class BindingCollector extends ASTVisitor { BindingCollector() { super(true); } @Override public int leave(IASTDeclaration declaration) { if (isPartOfExternalMacroDefinition(declaration)) return PROCESS_CONTINUE; if (declaration instanceof IASTSimpleDeclaration) { /* * The type specifier of a simple declaration of a variable must always be defined * except within the situations shown by the following examples: * * Example 1: * X* x; // definition of X is not required here - pointer type * * Example 2: * X& x; // definition of X is not required here - reference type * * The type specifier of a simple function declaration also doesn't need to be * defined: * * Example 3: * X foo(); // definition of X is not required here * * The type specifier of static member declarations also doesn't need to be defined: * * Example 4: * class Y { * static X x; // definition of X is not required here * }; */ IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); if (declSpecifier instanceof IASTNamedTypeSpecifier) { // We only handle simple declarations here whose declaration specifiers are // named type specifiers. boolean staticMember = simpleDeclaration.getParent() instanceof IASTCompositeTypeSpecifier && declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static; // Declare the named type specifier if all declarators are either pointers, // references or functions. boolean canBeDeclared = true; if (!staticMember) { for (IASTDeclarator declarator : declarators) { if (!(declarator instanceof IASTFunctionDeclarator) && declarator.getPointerOperators().length == 0) { canBeDeclared = false; break; } } } if (!canBeDeclared) { IASTName name = ((IASTNamedTypeSpecifier) declSpecifier).getName(); defineBindingForName(name); } } } return PROCESS_CONTINUE; } @Override public int leave(IASTDeclarator declarator) { if (isPartOfExternalMacroDefinition(declarator)) return PROCESS_CONTINUE; if (declarator instanceof IASTFunctionDeclarator) { /* * The type specifier of a function definition doesn't need to be defined if it is * a pointer or a reference type. * * Example 1: * X *foo() { } // definition of X is not required here * * Example 2: * X& foo() { } // definition of X is not required here */ IASTName name = declarator.getPropertyInParent() == ICPPASTLambdaExpression.DECLARATOR ? ((ICPPASTLambdaExpression) declarator.getParent()).getFunctionCallOperatorName() : declarator.getName(); IBinding binding = name.resolveBinding(); if (binding instanceof IFunction) { IFunction function = (IFunction) binding; IFunctionType functionType = function.getType(); if (declarator.getPropertyInParent() == IASTFunctionDefinition.DECLARATOR || declarator.getPropertyInParent() == ICPPASTLambdaExpression.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 = 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 leave(IASTDeclSpecifier declSpec) { if (isPartOfExternalMacroDefinition(declSpec)) return PROCESS_CONTINUE; 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 leave(ICPPASTBaseSpecifier baseSpecifier) { if (isPartOfExternalMacroDefinition(baseSpecifier)) return PROCESS_CONTINUE; /* * The type of a base specifier must always be defined. * * Example: * class Y : X {}; // definition of X is required here */ defineBinding(baseSpecifier.getNameSpecifier().resolveBinding()); return PROCESS_CONTINUE; } @Override public int leave(IASTInitializer initializer) { if (isPartOfExternalMacroDefinition(initializer)) return PROCESS_CONTINUE; /* * The type of the member of a constructor chain initializer doesn't need to be defined * if it is a pointer or reference type and if the argument type matches. * * Example 1: * class X { * X* x1; * X(X* x2) : * x1(x2) {} // definition of typeof(x1) is not required here * }; * * Example 2: * class X { * X& x1; * X(X& x2) : * x1(x2) {} // definition of typeof(x1) is not required here * }; * * The type of a constructor initializer doesn't need to be defined if it matches with * the type of the declaration. * * Example: * void foo(X& x1) { * X& x2(x1); // definition of typeof(x1) is not required here * } * * The type of an equals initializer doesn't need to be defined if it matches with * the type of the declaration. * * Example: * void foo(X& x1) { * X& x2 = x1; // definition of typeof(x1) is not required here * } */ // Get the binding of the initialized AST name first. IASTNode variableNode = initializer; IASTName variableName = null; IBinding variableBinding = null; while (variableNode != null) { if (variableNode instanceof IASTDeclarator) { variableName = ((IASTDeclarator) variableNode).getName(); break; } else if (variableNode instanceof ICPPASTConstructorChainInitializer) { variableName = ((ICPPASTConstructorChainInitializer) variableNode).getMemberInitializerId(); break; } variableNode = variableNode.getParent(); } if (variableName != null) variableBinding = variableName.resolveBinding(); // Get the arguments of the initializer. IASTInitializerClause[] arguments = IASTExpression.EMPTY_EXPRESSION_ARRAY; if (initializer instanceof ICPPASTConstructorInitializer) { ICPPASTConstructorInitializer constructorInitializer = (ICPPASTConstructorInitializer) initializer; arguments = constructorInitializer.getArguments(); } else if (initializer instanceof IASTEqualsInitializer) { IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer; arguments = new IASTInitializerClause[] { equalsInitializer.getInitializerClause() }; } if (variableBinding instanceof IVariable) { // Variable construction. boolean defineVariableType = true; if (initializer.getPropertyInParent() == IASTDeclarator.INITIALIZER) { IASTDeclarator declarator = (IASTDeclarator) initializer.getParent(); IASTDeclSpecifier declSpec = getDeclarationSpecifier(declarator); if (declSpec != null && isPartOfExternalMacroDefinition(declSpec)) defineVariableType = false; } IType targetType = ((IVariable) variableBinding).getType(); if (!(targetType instanceof IPointerType || targetType instanceof ICPPReferenceType)) { if (defineVariableType) { // We're constructing a non-pointer type. We need to define the member type // either way since we must be able to call its constructor. defineTypeExceptTypedefOrNonFixedEnum(targetType); } // TODO: Process the arguments. But how to get the corresponding IParameter[] array here? // processParameters(declaredParameters, arguments); } else { // We're constructing a pointer type. No constructor is called. We however have // to check whether the argument type matches the declared type. for (IASTInitializerClause argument : arguments) { if (argument instanceof IASTExpression) { IASTExpression expression = (IASTExpression) argument; IType argumentType = expression.getExpressionType(); if (targetType instanceof ICPPReferenceType && expression instanceof IASTUnaryExpression && ((IASTUnaryExpression) expression).getOperator() == IASTUnaryExpression.op_star) { argumentType = new CPPReferenceType(argumentType, false); } if (isTypeDefinitionRequiredForConversion(argumentType, targetType)) { // Types don't match. Define both types. if (defineVariableType) defineTypeExceptTypedefOrNonFixedEnum(targetType); defineTypeExceptTypedefOrNonFixedEnum(argumentType); } } } } } return PROCESS_CONTINUE; } @Override public int leave(IASTStatement statement) { if (isPartOfExternalMacroDefinition(statement)) return PROCESS_CONTINUE; if (statement instanceof IASTReturnStatement) { /* * The type of the return value expression doesn't need to be defined if the actual * return type matches the declared return type (i.e. if no implicit conversion is * necessary). * * Example: * X& foo(X& x) { * return x; // definition of typeof(x) is not required here * } */ IASTReturnStatement returnStatement = (IASTReturnStatement) statement; IASTExpression returnValue = returnStatement.getReturnValue(); if (returnValue != null) { // Get the containing function definition. IASTNode functionDefinitionNode = returnStatement; while (functionDefinitionNode != null && !(functionDefinitionNode instanceof IASTFunctionDefinition)) { functionDefinitionNode = functionDefinitionNode.getParent(); } // Check whether the declared return type matches the actual return type. if (functionDefinitionNode != null) { IASTFunctionDefinition functionDefinition = (IASTFunctionDefinition) functionDefinitionNode; IASTFunctionDeclarator functionDeclarator = functionDefinition.getDeclarator(); if (functionDeclarator != null) { IBinding binding = functionDeclarator.getName().resolveBinding(); if (binding instanceof IFunction) { IFunction function = (IFunction) binding; // Get the declared return type and the actual expression type. IType returnType = function.getType().getReturnType(); IType returnValueType = returnValue.getExpressionType(); if (isTypeDefinitionRequiredForConversion(returnValueType, returnType)) { // Both types have to be defined for conversion. defineTypeExceptTypedefOrNonFixedEnum(returnType); defineTypeExceptTypedefOrNonFixedEnum(returnValueType); } } } } } } else if (statement instanceof ICPPASTCatchHandler) { /* * Catch handles always need the definition of the caught type, even if it's a pointer * or reference type. * * Example: * void foo() { * try { * } catch (X& x) { // Definition of X is required here * } * } */ ICPPASTCatchHandler catchHandler = (ICPPASTCatchHandler) statement; IASTDeclaration declaration = catchHandler.getDeclaration(); if (declaration instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; IASTDeclSpecifier declSpecifier = simpleDeclaration.getDeclSpecifier(); if (declSpecifier instanceof IASTNamedTypeSpecifier) { IASTNamedTypeSpecifier namedTypeSpecifier = (IASTNamedTypeSpecifier) declSpecifier; defineBindingForName(namedTypeSpecifier.getName()); } } } return PROCESS_CONTINUE; } @Override public int leave(IASTExpression expression) { if (isPartOfExternalMacroDefinition(expression)) return PROCESS_CONTINUE; ASTNodeProperty propertyInParent = expression.getPropertyInParent(); if (propertyInParent == IASTIfStatement.CONDITION || propertyInParent == IASTForStatement.CONDITION || propertyInParent == IASTWhileStatement.CONDITIONEXPRESSION || propertyInParent == IASTDoStatement.CONDITION || propertyInParent == IASTConditionalExpression.LOGICAL_CONDITION) { /* * The type of the condition expression doesn't need to be defined if it's * a pointer type. * * Example: * void foo(X* x) { * if (x) { } // definition of typeof(x) is not required here * } */ IType conditionExpressionType = expression.getExpressionType(); if (!(conditionExpressionType instanceof IPointerType)) { defineTypeExceptTypedefOrNonFixedEnum(conditionExpressionType); } } if (expression instanceof IASTIdExpression) { /* * The type of an identifier expression doesn't need to be defined if it's a pointer * or a reference type. * * Example: * void foo(X& x) { * x; // definition of typeof(x) is not required here * } */ IASTIdExpression idExpression = (IASTIdExpression) expression; IBinding binding = idExpression.getName().resolveBinding(); if (binding instanceof IVariable) { // Get the declared type. IType variableType = ((IVariable) binding).getType(); defineTypeForBinding(binding, variableType); } } else if (expression instanceof IASTUnaryExpression) { /* * The type of the operand of an unary expression doesn't need to be defined if * the operator is the ampersand operator: * * Example: * void foo(X& x) { * &x; // ampersand operator * } * * If the operand is a pointer type, the following operators also don't require * a definition: * * Example: * void foo(X* x) { * __alignof(x); // alignof operator * !x; // not operator * +x; // unary plus operator * sizeof(x); // sizeof operator * typeid(x); // typeid operator * } */ IASTUnaryExpression unaryExpression = (IASTUnaryExpression) expression; IASTExpression operand = unaryExpression.getOperand(); if (operand != null) { // A throw expression may have no operand. if (unaryExpression instanceof ICPPASTUnaryExpression) { ICPPFunction overload = ((ICPPASTUnaryExpression) unaryExpression).getOverload(); if (overload != null) { defineForFunctionCall(overload, true, new IASTInitializerClause[] { operand }); return PROCESS_CONTINUE; } } boolean expressionDefinitionRequired = true; switch (unaryExpression.getOperator()) { case IASTUnaryExpression.op_amper: case IASTUnaryExpression.op_bracketedPrimary: // The ampersand operator as well as brackets never require a definition. expressionDefinitionRequired = false; break; case IASTUnaryExpression.op_star: if (expression.getParent() instanceof IASTExpression) break; //$FALL-THROUGH$ case IASTUnaryExpression.op_alignOf: case IASTUnaryExpression.op_not: case IASTUnaryExpression.op_plus: case IASTUnaryExpression.op_sizeof: case IASTUnaryExpression.op_typeid: // If the operand is a pointer type, then it doesn't need to be defined. if (operand.getExpressionType() instanceof IPointerType) { expressionDefinitionRequired = false; } break; } if (expressionDefinitionRequired) { defineTypeExceptTypedefOrNonFixedEnum(operand.getExpressionType()); } } } else if (expression instanceof IASTBinaryExpression) { /* * The types of the operands of a binary expression don't need to be defined for * the following operators if the operands are pointer types: * * Example: * void foo(X* x) { * x = x; // assignment operator * x == x; // equals operator * x != x; // not equals operator * x >= x; // greater equal operator * x > x; // greater operator * x <= x; // less equal operator * x < x; // less operator * x && x; // logical and operator * x || x; // logical or operator * } * * However, note that if both operands are pointers of different types, then only * the following operators don't require a definition of the types of the operands: * * void foo(X* x, Y* y) { * x && y; // logical and operator * x || y; // logical or operator * } */ IASTBinaryExpression binaryExpression = (IASTBinaryExpression) expression; if (binaryExpression instanceof ICPPASTBinaryExpression) { ICPPFunction overload = ((ICPPASTBinaryExpression) binaryExpression).getOverload(); if (overload != null) { IASTInitializerClause[] arguments = new IASTInitializerClause[] { binaryExpression.getOperand1(), binaryExpression.getOperand2() }; defineForFunctionCall(overload, true, arguments); return PROCESS_CONTINUE; } } IType operand1Type = binaryExpression.getOperand1().getExpressionType(); IASTInitializerClause operand2 = binaryExpression.getInitOperand2(); IType operand2Type; if (operand2 instanceof IASTExpression) { operand2Type = ((IASTExpression) operand2).getExpressionType(); } else if (operand2 instanceof ICPPASTInitializerList) { ICPPASTInitializerList initializerList = (ICPPASTInitializerList) operand2; if (binaryExpression.getOperator() == IASTBinaryExpression.op_assign && initializerList.getSize() == 1) { IASTInitializerClause element = initializerList.getClauses()[0]; if (element instanceof IASTExpression) { operand2Type = ((IASTExpression) element).getExpressionType(); } else { operand2Type = initializerList.getEvaluation().getType(operand2); } } else { operand2Type = initializerList.getEvaluation().getType(operand2); } } else { operand2Type = operand1Type; } boolean expression1DefinitionRequired = true; boolean expression2DefinitionRequired = true; switch (binaryExpression.getOperator()) { case IASTBinaryExpression.op_logicalAnd: case IASTBinaryExpression.op_logicalOr: // Pointer types don't need to be defined for logical operations. if (operand1Type instanceof IPointerType) { expression1DefinitionRequired = false; } if (operand2Type instanceof IPointerType) { expression2DefinitionRequired = false; } break; case IASTBinaryExpression.op_assign: case IASTBinaryExpression.op_equals: case IASTBinaryExpression.op_notequals: case IASTBinaryExpression.op_greaterEqual: case IASTBinaryExpression.op_greaterThan: case IASTBinaryExpression.op_lessEqual: case IASTBinaryExpression.op_lessThan: // If both operands are identical pointer types, then they don't need to be defined. if (operand1Type instanceof IPointerType && operand2Type instanceof IPointerType) { if (!isTypeDefinitionRequiredForConversion(operand2Type, operand1Type)) { expression1DefinitionRequired = false; expression2DefinitionRequired = false; } } else if (operand1Type instanceof IPointerType) { // Only the first operand is a pointer type. It doesn't have to be defined. expression1DefinitionRequired = false; } else if (operand2Type instanceof IPointerType) { // Only the second operand is a pointer type. It doesn't have to be defined. expression2DefinitionRequired = false; } } if (expression1DefinitionRequired) { defineTypeExceptTypedefOrNonFixedEnum(operand1Type); } if (expression2DefinitionRequired) { defineTypeExceptTypedefOrNonFixedEnum(operand2Type); } } else if (expression instanceof IASTFunctionCallExpression) { /* * The return type and argument types of a function call expression don't need to be * defined if they're pointer or reference types. The declared and actual types of * the arguments must further be identical, since implicit type conversions require * a definition of both the source and the target type. * * Example: * X& foo(X& x); * void bar(X& x) { * foo(x); // definition of typeof(foo) and typeof(x) is not required here * } * * Also note that the function call itself doesn't require a definition as long as * it's not a constructor call: * * Example 1: * void foo() { * bar(); // definition of bar() is not required here (assuming bar is a function) * } * * Example 2: * void foo() { * X(); // definition of X is required here (assuming X is a composite type) * } */ IASTFunctionCallExpression functionCallExpression = (IASTFunctionCallExpression) expression; IASTExpression functionNameExpression = functionCallExpression.getFunctionNameExpression(); if (isPartOfExternalMacroDefinition(functionNameExpression)) return PROCESS_CONTINUE; IASTInitializerClause[] arguments = functionCallExpression.getArguments(); IASTName functionName = getNameOfIdOrFieldReferenceExpression(functionNameExpression); if (functionName != null) { IBinding function = functionName.resolveBinding(); if (function instanceof IProblemBinding) { IBinding[] candidates = ((IProblemBinding) function).getCandidateBindings(); if (candidates.length != 0) { for (IBinding candidate : candidates) { defineBindingForFunctionCall(candidate, true, arguments); } } else { defineBinding(function); } } else { IASTName name = functionName; if (functionName instanceof ICPPASTTemplateId) { name = ((ICPPASTTemplateId) functionName).getTemplateName(); } boolean defineFunction = !isPartOfExternalMacroDefinition(name); if (defineFunction) { LookupData data = new LookupData(functionName); IType impliedObjectType = data.getImpliedObjectType(); if (impliedObjectType != null) defineTypeExceptTypedefOrNonFixedEnum(impliedObjectType); } defineBindingForFunctionCall(function, defineFunction, arguments); } } if (functionCallExpression instanceof IASTImplicitNameOwner) { IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) functionCallExpression).getImplicitNames(); for (IASTName name : implicitNames) { IBinding binding = name.resolveBinding(); if (binding instanceof IFunction) { defineForFunctionCall((IFunction) binding, true, arguments); } } } } else if (expression instanceof IASTFieldReference) { /* * The type of the expression part of a field reference always requires a definition. * * Example: * void foo(X& x1, X* x2) { * x1.bar(); // definition of typeof(x1) is required here * x2->bar(); // definition of typeof(x2) is required here * } */ IASTExpression fieldOwner = ((IASTFieldReference) expression).getFieldOwner(); IType expressionType = fieldOwner.getExpressionType(); defineIndirectTypes(expressionType); IASTName name = getNameOfIdOrFieldReferenceExpression(fieldOwner); if (name != null) { IBinding binding = name.resolveBinding(); defineTypeForBinding(binding, expressionType); } } else if (expression instanceof ICPPASTNewExpression) { /* * The type specifier of a "new" expression always requires a definition. * * Example: * void foo() { * new X(); // definition of X is required here * } */ defineTypeExceptTypedefOrNonFixedEnum(((ICPPASTNewExpression) expression).getExpressionType()); } else if (expression instanceof ICPPASTDeleteExpression) { /* * The expression type of a "delete" expression always requires a full definition. * This is necessary because the compiler needs to be able to call the destructor. * * Example: * void foo(X* x) { * delete x; // definition of typeof(x) is required here * } */ defineTypeExceptTypedefOrNonFixedEnum(((ICPPASTDeleteExpression) expression).getOperand().getExpressionType()); } else if (expression instanceof IASTCastExpression) { /* * Explicit type casts always need the definition of the underlying types. * * Example: * void foo(X* x) { * (Y*) x; // definition of both Y and typeof(x) is required here * } */ IASTCastExpression castExpression = (IASTCastExpression) expression; IType targetType = castExpression.getExpressionType(); IType sourceType = castExpression.getOperand().getExpressionType(); if (isTypeDefinitionRequiredForConversion(sourceType, targetType)) { // Source and target types of the cast expression are different. // We need to define both types, even if they're pointers. defineTypeExceptTypedefOrNonFixedEnum(targetType); defineTypeExceptTypedefOrNonFixedEnum(sourceType); } else if (!(targetType instanceof IPointerType || targetType instanceof ICPPReferenceType)) { // Define the target type if it's not a pointer or reference type. defineTypeExceptTypedefOrNonFixedEnum(targetType); } } return PROCESS_CONTINUE; } protected void defineBindingForFunctionCall(IBinding binding, boolean defineFunction, IASTInitializerClause[] arguments) { if (binding instanceof IFunction) { defineForFunctionCall((IFunction) binding, defineFunction, arguments); } else if (defineFunction) { if (binding instanceof ICPPMember) { try { IType memberType = ((ICPPMember) binding).getType(); defineIndirectTypes(memberType); } catch (DOMException e) { } } else if (binding instanceof ITypedef) { defineBinding(binding); } } } @Override public int leave(IASTName name) { if (name instanceof ICPPASTQualifiedName || name instanceof ICPPASTTemplateId) { return PROCESS_CONTINUE; } if (isPartOfExternalMacroDefinition(name)) return PROCESS_CONTINUE; // Add the binding associated with the name to the bindings that can be declared // (we assume that all bindings which have to be defined are already explicitly handled // elsewhere). if (name instanceof ICPPASTQualifiedName) { // All qualifying names must be defined. ICPPASTNameSpecifier[] qualifier = ((ICPPASTQualifiedName) name).getQualifier(); for (ICPPASTNameSpecifier nameSpec : qualifier) { defineBinding(nameSpec.resolveBinding()); } } IBinding binding = name.resolveBinding(); if (binding != null) { if (isTemplateArgumentRequiringCompleteType(name)) { // The name is part of a template argument - define the corresponding binding. defineBinding(binding); } else if (name.getPropertyInParent() != IASTFieldReference.FIELD_NAME) { // Field references are handled separately. IBinding owner = binding.getOwner(); if (owner instanceof IType) { defineBinding(owner); // Member access requires definition of the containing type. if (binding instanceof IProblemBinding) declareBinding(binding); if (!isDefinedLocally(owner) && (!(binding instanceof ICPPMember) || !((ICPPMember) binding).isStatic())) { // Record the fact that the header file defining the owner must also // provide a definition of the type of this member (bug 442841). IType type = null; if (binding instanceof IVariable) { type = ((IVariable) binding).getType(); } else if (binding instanceof IFunction) { type = ((IFunction) binding).getType().getReturnType(); } type = getNestedType(type, ALLCVQ); if (type instanceof IBinding) { markAsDefined((IBinding) type); } } } else { // ID expressions are handled separately. declareBinding(binding); // Declare the binding of this name. } } } return PROCESS_CONTINUE; } @Override public int leave(IASTTranslationUnit tu) { for (IASTPreprocessorMacroExpansion macroExpansion : tu.getMacroExpansions()) { IASTPreprocessorMacroDefinition macroDefinition = macroExpansion.getMacroDefinition(); IASTName name = macroDefinition.getName(); IMacroBinding macroBinding = (IMacroBinding) name.getBinding(); // Ignore trivial macros like '#define false false' if (!CharArrayUtils.equals(name.getSimpleID(), macroBinding.getExpansion())) { defineBinding(macroBinding); } } return PROCESS_CONTINUE; } } private static class LocalNameFinder extends ASTVisitor { boolean found; LocalNameFinder() { shouldVisitNames = true; } @Override public int visit(IASTName name) { if (!(name instanceof ICPPASTQualifiedName)) { IASTImageLocation imageLocation = name.getImageLocation(); if (imageLocation != null && imageLocation.getFileName().equals(name.getTranslationUnit().getFilePath())) { found = true; return PROCESS_ABORT; } } return PROCESS_CONTINUE; } } private final IncludeCreationContext fContext; private final IncludePreferences fPreferences; /** The bindings which require a full definition. */ private final Set<IBinding> fBindingsToDefine; /** The bindings which only require a simple forward declaration. */ private final Set<IBinding> fBindingsToForwardDeclare; /** The AST that the classifier is working on. */ private IASTTranslationUnit fAst; private final BindingCollector fBindingCollector; private final Set<IBinding> fProcessedDefinedBindings; private final Set<IBinding> fProcessedDeclaredBindings; /** * @param context the context for binding classification */ public BindingClassifier(IncludeCreationContext context) { fContext = context; fPreferences = context.getPreferences(); fBindingsToDefine = new HashSet<>(); fBindingsToForwardDeclare = new HashSet<>(); fProcessedDefinedBindings = new HashSet<>(); fProcessedDeclaredBindings = new HashSet<>(); fBindingCollector = new BindingCollector(); } public void classifyNodeContents(IASTNode node) { if (fAst == null) { fAst = node.getTranslationUnit(); } node.accept(fBindingCollector); } /** * Returns the bindings which require a full definition. */ public Set<IBinding> getBindingsToDefine() { return fBindingsToDefine; } /** * Returns the bindings which only require a simple forward declaration. */ public Set<IBinding> getBindingsToForwardDeclare() { return fBindingsToForwardDeclare; } /** * Defines the required types of the parameters of a function or constructor call expression by * comparing the declared parameters with the actual arguments. */ private void processFunctionParameters(IFunction function, boolean defineFunction, IASTInitializerClause[] arguments) { boolean functionIsDeclared = fProcessedDefinedBindings.contains(function); IParameter[] parameters = function.getParameters(); for (int i = 0; i < parameters.length && i < arguments.length; i++) { IType parameterType = parameters[i].getType(); IASTInitializerClause argument = arguments[i]; if (argument instanceof IASTExpression) { IType argumentType = ((IASTExpression) argument).getExpressionType(); if (!isTypeDefinitionRequiredForConversion(argumentType, parameterType)) { // A declaration is sufficient if the argument type matches the parameter type. // We don't need to provide a declaration of the parameter type since it is // a responsibility of the header declaring the function. if (!functionIsDeclared && defineFunction) { declareType(parameterType); } continue; } // The type of the argument requires a full definition. defineTypeExceptTypedefOrNonFixedEnum(argumentType); } if (defineFunction) { // As a matter of policy, a header declaring the function is responsible for // defining parameter types that allow implicit conversion. parameterType = getNestedType(parameterType, REF | ALLCVQ); if (!(parameterType instanceof ICPPClassType) || fAst.getDeclarationsInAST(function).length != 0 || !hasConvertingConstructor((ICPPClassType) parameterType, argument)) { defineTypeExceptTypedefOrNonFixedEnum(parameterType); } else if (!functionIsDeclared) { declareType(parameterType); } } } } /** * Checks if the two given types have to be defined for the first type to be implicitly * converted to the second one. * * @param sourceType the type to be converted * @param targetType the type to be converted to * @return {@code true} if the types have to be defined */ private boolean isTypeDefinitionRequiredForConversion(IType sourceType, IType targetType) { if (!(targetType instanceof IPointerType || targetType instanceof ICPPReferenceType)) return true; if (targetType instanceof IPointerType && Conversions.isNullPointerConstant(sourceType)) return false; sourceType = getNestedType(sourceType, REF | ALLCVQ); targetType = getNestedType(targetType, REF | ALLCVQ); if (sourceType instanceof IPointerType && targetType instanceof IPointerType) { sourceType = getNestedType(((IPointerType) sourceType).getType(), ALLCVQ); targetType = getNestedType(((IPointerType) targetType).getType(), ALLCVQ); } return !sourceType.isSameType(targetType); } /** * 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) { CPPASTName astName = new CPPASTName(); astName.setName(classType.getNameCharArray()); astName.setOffsetAndLength((ASTNode) argument); CPPASTIdExpression idExp = new CPPASTIdExpression(astName); idExp.setParent(argument.getParent()); idExp.setPropertyInParent(IASTFunctionCallExpression.FUNCTION_NAME); LookupData lookupData = new LookupData(astName); lookupData.setFunctionArguments(false, new IASTInitializerClause[] { argument }); lookupData.qualified = true; try { IBinding constructor = CPPSemantics.resolveFunction(lookupData, ClassTypeHelper.getConstructors(classType, argument), false); if (constructor instanceof ICPPConstructor && !((ICPPConstructor) constructor).isExplicit()) return true; } catch (DOMException e) { } 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 = parameters[0].getType(); if (type instanceof IBasicType && ((IBasicType) type).getKind() == IBasicType.Kind.eVoid) continue; type = getNestedType(type, 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 * the binding for the type of the variable. This is because we actually have to declare or * define the type of a variable, not the variable itself. * * @param binding The binding to resolve. * @return The binding(s) which is/are suitable for either declaration or definition, * or an empty list if no such binding is available. */ private Set<IBinding> getRequiredBindings(IBinding binding) { if (binding instanceof ICPPNamespaceAlias) return Collections.singleton(binding); if (binding instanceof ICPPNamespace) return Collections.emptySet(); if (binding instanceof ICPPUsingDeclaration) { Set<IBinding> result = new HashSet<>(); Collections.addAll(result, ((ICPPUsingDeclaration) binding).getDelegates()); return result; } Deque<IBinding> queue = new ArrayDeque<>(); addRequiredBindings(binding, queue); Set<IBinding> bindings = new HashSet<>(); while ((binding = queue.poll()) != 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. std::hash and __gnu_cxx::hash are // exceptions from that rule. if (type instanceof IBinding && CharArrayUtils.equals(((IBinding) type).getNameCharArray(), "hash")) { //$NON-NLS-1$ IBinding owner = ((IBinding) type).getOwner(); if (owner instanceof ICPPNamespace && (CharArrayUtils.equals(owner.getNameCharArray(), STD) || CharArrayUtils.equals(owner.getNameCharArray(), "__gnu_cxx"))) //$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<Y>. addRequiredBindings(((ICPPSpecialization) binding).getSpecializedBinding(), queue); } else { bindings.add(binding); } } return bindings; } private void addRequiredBindings(IBinding binding, Deque<IBinding> newBindings) { if (binding instanceof IProblemBinding) { IProblemBinding problemBinding = (IProblemBinding) binding; IBinding[] candidateBindings = problemBinding.getCandidateBindings(); if (candidateBindings.length > 0) { // There are candidate bindings available. We simply use them all here since those // 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. 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); Collections.addAll(newBindings, indexMacros); } catch (CoreException e) { } } } else if (binding instanceof ICPPMethod) { newBindings.add(binding); // Include the method in case we need its inline definition. if (binding instanceof ICPPConstructor) newBindings.add(binding.getOwner()); } else if (binding instanceof IType) { // Remove type qualifiers. IBinding b = getTypeBinding((IType) binding); if (b != null) newBindings.add(b); } else { newBindings.add(binding); } } private void declareType(IType type) { IBinding binding = getTypeBinding(type); if (binding != null) declareBinding(binding); } /** * Adds the given binding to the list of bindings which have to be forward declared. * * @param binding The binding to add. */ private void declareBinding(IBinding binding) { if (binding instanceof ICPPNamespace && !(binding instanceof ICPPNamespaceAlias)) return; if (fProcessedDefinedBindings.contains(binding)) return; if (isDeclaredLocally(binding)) return; // Declared locally. if (!fProcessedDeclaredBindings.add(binding)) return; if (!canForwardDeclare(binding)) { defineBinding(binding); return; } Collection<IBinding> requiredBindings = getRequiredBindings(binding); for (IBinding requiredBinding : requiredBindings) { if (fBindingsToForwardDeclare.contains(requiredBinding) || fBindingsToDefine.contains(requiredBinding)) { return; } if (isDeclaredLocally(requiredBinding) || isDefinedLocally(requiredBinding)) { return; // Declared or defined locally. } if (canForwardDeclare(requiredBinding)) { if (requiredBinding == binding) { fBindingsToForwardDeclare.add(requiredBinding); } else { declareBinding(requiredBinding); } } else { if (requiredBinding == binding) { fBindingsToDefine.add(requiredBinding); } else { defineBinding(requiredBinding); } } } } /** * Checks whether the binding can be forward declared according to preferences. */ private boolean canForwardDeclare(IBinding binding) { boolean canDeclare = false; if (binding instanceof IProblemBinding && ((IProblemBinding) binding).getCandidateBindings().length != 0) { return true; // Return true to consider delegates later on. } if (binding instanceof ICPPUsingDeclaration) { return true; // Return true to consider delegates later on. } if (binding instanceof ICompositeType) { canDeclare = fPreferences.forwardDeclareCompositeTypes; } else if (binding instanceof IEnumeration) { canDeclare = fPreferences.forwardDeclareEnums && isEnumerationWithoutFixedUnderlyingType(binding); } else if (binding instanceof IFunction && !(binding instanceof ICPPMethod)) { canDeclare = fPreferences.forwardDeclareFunctions; } else if (binding instanceof IVariable) { if (((IVariable) binding).isExtern()) canDeclare = fPreferences.forwardDeclareExternalVariables; } if (canDeclare && !fPreferences.forwardDeclareTemplates && (binding instanceof ICPPTemplateDefinition || binding instanceof ICPPSpecialization)) { canDeclare = false; } return canDeclare; } /** * Checks whether the type may be forward declared according to language rules. */ private boolean mayBeForwardDeclared(IType type) { IBinding binding = getTypeBinding(type); if (binding == null) return false; if (!fPreferences.assumeTemplatesMayBeForwardDeclared && (binding instanceof ICPPTemplateDefinition || binding instanceof ICPPSpecialization)) { return false; } if (binding instanceof ICompositeType) return true; return binding instanceof ICPPEnumeration && ((ICPPEnumeration) binding).getFixedType() != null; } /** * Adds the given type to the list of bindings which have to be defined. Typedefs and * enumerations without fixed underlying type are skipped since they must be defined in the file * that references them by name. If the type is explicitly referenced in this translation unit, * it will be defined independently from this method. * * @param type The type to add. */ private void defineTypeExceptTypedefOrNonFixedEnum(IType type) { IBinding typeBinding = getTypeBinding(type); if (typeBinding != null && !(typeBinding instanceof ITypedef) && !isEnumerationWithoutFixedUnderlyingType(typeBinding)) { defineBinding(typeBinding); } } /** * 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 (binding instanceof ICPPNamespace && !(binding instanceof ICPPNamespaceAlias)) return; if (!markAsDefined(binding)) return; if (isDefinedLocally(binding)) return; // Defined locally. Collection<IBinding> requiredBindings = getRequiredBindings(binding); for (IBinding requiredBinding : requiredBindings) { fBindingsToForwardDeclare.remove(requiredBinding); if (requiredBinding != binding) { if (!markAsDefined(requiredBinding)) continue; if (isDefinedLocally(requiredBinding)) continue; // Defined locally. } fBindingsToDefine.add(requiredBinding); } } private boolean isDefinedLocally(IBinding binding) { return binding instanceof CPPClosureType || fAst.getDefinitionsInAST(binding).length != 0; } private boolean isDeclaredLocally(IBinding binding) { IASTName[] declarations = fAst.getDeclarationsInAST(binding); for (IASTName name : declarations) { IASTNode node = name; if (node.getPropertyInParent() == ICPPASTQualifiedName.SEGMENT_NAME) node = node.getParent(); if (node.getPropertyInParent() != ICPPASTUsingDeclaration.NAME) return true; } return false; } private void defineBindingForName(IASTName name) { IBinding binding = name.resolveBinding(); if (!isPartOfExternalMacroDefinition(name)) defineBinding(binding); } /** * 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. markAsDefined((IBinding) type); } } else if (binding instanceof ICPPClassType && fAst.getDefinitionsInAST(binding).length == 0) { // The header that defines a class must provide definitions of all its base classes. ICPPClassType[] bases = ClassTypeHelper.getAllBases((ICPPClassType) binding, fAst); for (ICPPClassType base : bases) { fProcessedDefinedBindings.add(base); fBindingsToDefine.remove(base); fBindingsToForwardDeclare.remove(base); } } return true; } private void defineForFunctionCall(IFunction function, boolean defineFunction, IASTInitializerClause[] arguments) { if (defineFunction) { if (!fProcessedDefinedBindings.contains(function)) { if (!(function instanceof ICPPMethod) && (!canForwardDeclare(function) || isDefinedInHeaderFile(function))) { // Since the function is defined in a header file, its definition has to be // reachable through includes to make a function call. defineBinding(function); } else { declareBinding(function); } } // Handle return or expression type of the function or constructor call. IType returnType = function.getType().getReturnType(); defineTypeForBinding(function, returnType); } // Handle parameters. processFunctionParameters(function, defineFunction, arguments); fProcessedDefinedBindings.add(function); } private boolean isDefinedInHeaderFile(IFunction function) { try { IIndexName[] definitions = fContext.getIndex().findDefinitions(function); for (IIndexName name : definitions) { IIndexFile file = name.getFile(); if (file != null && canBeIncluded(file)) return true; } } catch (CoreException e) { // Ignore to return false. } return false; } private boolean canBeIncluded(IIndexFile indexFile) throws CoreException { return !IncludeUtil.isSource(indexFile, fContext.getProject()) || fContext.getIndex().findIncludedBy(indexFile, 0).length != 0; } private void defineTypeForBinding(IBinding binding, IType type) { if (isDefined(binding) && !mayBeForwardDeclared(type)) { defineIndirectTypes(type); } else if (!(type instanceof IPointerType || type instanceof ICPPReferenceType)) { defineTypeExceptTypedefOrNonFixedEnum(type); } } /** * For a pointer or a reference type, defines the contained type. For an instance of a template * allowing incomplete argument types, defines the argument type. */ private void defineIndirectTypes(IType type) { IType resolvedType = removeQualifiers(resolveTypedef(type)); if (resolvedType instanceof IPointerType || resolvedType instanceof ICPPReferenceType) { defineTypeExceptTypedefOrNonFixedEnum(resolvedType); } else { if (resolvedType instanceof ICPPTemplateInstance) { ICPPTemplateInstance instance = (ICPPTemplateInstance) resolvedType; IBinding template = instance.getSpecializedBinding(); if (isTemplateAllowingIncompleteArgumentType(template)) { ICPPTemplateArgument[] arguments = instance.getTemplateArguments(); if (arguments.length != 0) { IType argumentType = arguments[0].getTypeValue(); defineTypeExceptTypedefOrNonFixedEnum(argumentType); } } } } } private boolean isDefined(IBinding binding) { if (fBindingsToDefine.contains(binding)) return true; IBinding owner = binding.getOwner(); if (owner instanceof IType && fBindingsToDefine.contains(owner)) return true; 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; } /** * Returns the name corresponding to the ID or the field reference expression. */ private static IASTName getNameOfIdOrFieldReferenceExpression(IASTExpression expression) { if (expression instanceof IASTIdExpression) { return ((IASTIdExpression) expression).getName(); } else if (expression instanceof IASTFieldReference) { return ((IASTFieldReference) expression).getFieldName(); } return null; } /** * 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; } /** * If the given type is a typedef, returns the ultimate type the typedef is pointing to. * Otherwise returns the given type. */ private IType resolveTypedef(IType type) { while (type instanceof ITypedef) { type = ((ITypedef) type).getType(); } return type; } /** * If the given type is a qualified type, returns the corresponding unqualified type. * Otherwise returns the given type. */ private IType removeQualifiers(IType type) { while (type instanceof IQualifierType) { type = ((IQualifierType) type).getType(); } return type; } /** * Checks if the given name is part of a template argument. */ private boolean isTemplateArgumentRequiringCompleteType(IASTName name) { ICPPASTTypeId typeId = ASTQueries.findAncestorWithType(name, ICPPASTTypeId.class); if (typeId == null || typeId.getPropertyInParent() != ICPPASTTemplateId.TEMPLATE_ID_ARGUMENT) return false; ICPPASTTemplateId templateId = (ICPPASTTemplateId) typeId.getParent(); IBinding template = templateId.resolveBinding(); if (template instanceof IProblemBinding) return true; IBinding owner = template.getOwner(); if (!(owner instanceof ICPPNamespace) || !CharArrayUtils.equals(owner.getNameCharArray(), STD) || owner.getOwner() != null) { return true; } if (!isTemplateAllowingIncompleteArgumentType(template)) return true; // For most templates allowing incomplete argument type a full definition of the argument // type is required if the destructor is called. Since the AST doen't contain all destructor // calling points, we have to use an indirect approach by examining the containing scope. IASTNode parent = templateId.getParent(); if (!(parent instanceof ICPPASTNamedTypeSpecifier)) return false; parent = parent.getParent(); if (!(parent instanceof IASTSimpleDeclaration)) return true; parent = parent.getParent(); if (!(parent instanceof ICPPASTCompositeTypeSpecifier)) return true; ICPPClassScope classScope = ((ICPPASTCompositeTypeSpecifier) parent).getScope(); ICPPClassType classType = classScope.getClassType(); ICPPMethod destructor = ClassTypeHelper.getMethodInClass(classType, MethodKind.DTOR, parent); if (destructor != null && fAst.getDefinitionsInAST(destructor).length != 0) return true; return false; } private static boolean isEnumerationWithoutFixedUnderlyingType(IBinding typeBinding) { return typeBinding instanceof IEnumeration && (!(typeBinding instanceof ICPPEnumeration) || ((ICPPEnumeration) typeBinding).getFixedType() == null); } private static boolean isPartOfExternalMacroDefinition(IASTNode node) { IASTNodeLocation[] locations = node.getNodeLocations(); if (locations.length != 1 || !(locations[0] instanceof IASTMacroExpansionLocation)) return false; IASTMacroExpansionLocation macroExpansionLocation = (IASTMacroExpansionLocation) locations[0]; IASTPreprocessorMacroExpansion macroExpansion = macroExpansionLocation.getExpansion(); IASTPreprocessorMacroDefinition macroDefinition = macroExpansion.getMacroDefinition(); if (macroDefinition.isPartOfTranslationUnitFile()) return false; if (!(macroDefinition instanceof IASTPreprocessorFunctionStyleMacroDefinition)) return true; return !containsNameFromTranslationUnit(node); } private static boolean containsNameFromTranslationUnit(IASTNode node) { LocalNameFinder localNameFinder = new LocalNameFinder(); node.accept(localNameFinder); return localNameFinder.found; } /** * Returns the declaration specifier node for the given declarator. */ private static IASTDeclSpecifier getDeclarationSpecifier(IASTDeclarator declarator) { declarator= CPPVisitor.findOutermostDeclarator(declarator); IASTNode parent = declarator.getParent(); IASTDeclSpecifier declSpec = null; if (parent instanceof IASTSimpleDeclaration) { declSpec = ((IASTSimpleDeclaration) parent).getDeclSpecifier(); } else if (parent instanceof IASTParameterDeclaration) { declSpec = ((IASTParameterDeclaration) parent).getDeclSpecifier(); } else if (parent instanceof IASTFunctionDefinition) { declSpec = ((IASTFunctionDefinition) parent).getDeclSpecifier(); } else if (parent instanceof ICPPASTTypeId) { declSpec = ((ICPPASTTypeId) parent).getDeclSpecifier(); } return declSpec; } }