/*******************************************************************************
 * Copyright (c) 2005, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Andrew Niefer (IBM Rational Software) - Initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *     Bryan Wilkinson (QNX)
 *     Andrew Ferguson (Symbian)
 *     Jens Elmenthaler - http://bugs.eclipse.org/173458 (camel case completion)
 *     Sergey Prigogin (Google)
 *     Marc-Andre Laperle (Ericsson)
 *     Anders Dahlberg (Ericsson) - bug 84144
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.c;

import java.util.ArrayList;
import java.util.List;

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.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTAttribute;
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.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
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.IASTGotoStatement;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTLabelStatement;
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.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemHolder;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
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.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.ILabel;
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.IScope;
import org.eclipse.cdt.core.dom.ast.IScope.ScopeLookupData;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.c.ICASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTFieldDesignator;
import org.eclipse.cdt.core.dom.ast.c.ICASTPointer;
import org.eclipse.cdt.core.dom.ast.c.ICASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTTypedefNameSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICArrayType;
import org.eclipse.cdt.core.dom.ast.c.ICCompositeTypeScope;
import org.eclipse.cdt.core.dom.ast.c.ICFunctionScope;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.AttributeUtil;
import org.eclipse.cdt.core.parser.util.CharArraySet;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.core.parser.util.IContentAssistMatcher;
import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
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.IASTInternalScope;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator;
import org.eclipse.cdt.internal.core.dom.parser.ValueFactory;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;

/**
 * Collection of methods to find information in an AST.
 */
public class CVisitor extends ASTQueries {
	private static final CBasicType UNSIGNED_LONG_INT = new CBasicType(Kind.eInt,
			IBasicType.IS_LONG | IBasicType.IS_UNSIGNED);

	// Flags for createType().

	// Given a function declarator, compute only the return type rather than
	// the entire function type.
	public static final int ONLY_RETURN_TYPE = 0x1;

	public static class CollectProblemsAction extends ASTVisitor {
		{
			shouldVisitDeclarations = true;
			shouldVisitExpressions = true;
			shouldVisitStatements = true;
			shouldVisitTypeIds = true;
		}

		private static final int DEFAULT_CHILDREN_LIST_SIZE = 8;
		private IASTProblem[] problems = null;
		int numFound = 0;

		public CollectProblemsAction() {
			problems = new IASTProblem[DEFAULT_CHILDREN_LIST_SIZE];
		}

		private void addProblem(IASTProblem problem) {
			if (problems.length == numFound) { // if the found array is full, then double the array
				IASTProblem[] old = problems;
				problems = new IASTProblem[old.length * 2];
				for (int j = 0; j < old.length; ++j)
					problems[j] = old[j];
			}
			problems[numFound++] = problem;
		}

		private IASTProblem[] removeNullFromProblems() {
			if (problems[problems.length - 1] != null) { // if the last element in the list is not null then return the list
				return problems;
			} else if (problems[0] == null) { // if the first element in the list is null, then return empty list
				return new IASTProblem[0];
			}

			IASTProblem[] results = new IASTProblem[numFound];
			for (int i = 0; i < results.length; i++)
				results[i] = problems[i];

			return results;
		}

		public IASTProblem[] getProblems() {
			return removeNullFromProblems();
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processDeclaration(org.eclipse.cdt.core.dom.ast.IASTDeclaration)
		 */
		@Override
		public int visit(IASTDeclaration declaration) {
			if (declaration instanceof IASTProblemHolder)
				addProblem(((IASTProblemHolder) declaration).getProblem());

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processExpression(org.eclipse.cdt.core.dom.ast.IASTExpression)
		 */
		@Override
		public int visit(IASTExpression expression) {
			if (expression instanceof IASTProblemHolder)
				addProblem(((IASTProblemHolder) expression).getProblem());

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processStatement(org.eclipse.cdt.core.dom.ast.IASTStatement)
		 */
		@Override
		public int visit(IASTStatement statement) {
			if (statement instanceof IASTProblemHolder)
				addProblem(((IASTProblemHolder) statement).getProblem());

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processTypeId(org.eclipse.cdt.core.dom.ast.IASTTypeId)
		 */
		@Override
		public int visit(IASTTypeId typeId) {
			if (typeId instanceof IASTProblemHolder)
				addProblem(((IASTProblemHolder) typeId).getProblem());

			return PROCESS_CONTINUE;
		}
	}

	public static class CollectDeclarationsAction extends ASTVisitor {
		{
			shouldVisitDeclarators = true;
			shouldVisitDeclSpecifiers = true;
			shouldVisitEnumerators = true;
			shouldVisitStatements = true;
		}

		private static final int DEFAULT_CHILDREN_LIST_SIZE = 8;
		private IASTName[] declsFound = null;
		int numFound = 0;
		IBinding binding = null;
		boolean compositeTypeDeclared = false;

		private void addName(IASTName name) {
			if (declsFound.length == numFound) { // if the found array is full, then double the array
				IASTName[] old = declsFound;
				declsFound = new IASTName[old.length * 2];
				for (int j = 0; j < old.length; ++j)
					declsFound[j] = old[j];
			}
			declsFound[numFound++] = name;
		}

		private IASTName[] removeNullFromNames() {
			if (declsFound[declsFound.length - 1] != null) { // if the last element in the list is not null then return the list
				return declsFound;
			} else if (declsFound[0] == null) { // if the first element in the list is null, then return empty list
				return new IASTName[0];
			}

			IASTName[] results = new IASTName[numFound];
			for (int i = 0; i < results.length; i++)
				results[i] = declsFound[i];

			return results;
		}

		public IASTName[] getDeclarationNames() {
			return removeNullFromNames();
		}

		public CollectDeclarationsAction(IBinding binding) {
			declsFound = new IASTName[DEFAULT_CHILDREN_LIST_SIZE];
			this.binding = binding;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processDeclarator(org.eclipse.cdt.core.dom.ast.IASTDeclarator)
		 */
		@Override
		public int visit(IASTDeclarator declarator) {
			//GCC allows declarations in expressions, so we have to continue from the
			//declarator in case there is something in the initializer expression
			if (declarator == null || declarator.getName() == null || declarator.getName().toCharArray().length == 0)
				return PROCESS_CONTINUE;

			//if the binding is something not declared in a declarator, continue
			if (binding instanceof ICompositeType)
				return PROCESS_CONTINUE;
			if (binding instanceof IEnumeration)
				return PROCESS_CONTINUE;

			IASTNode parent = declarator.getParent();
			while (parent != null && !(parent instanceof IASTDeclaration || parent instanceof IASTParameterDeclaration))
				parent = parent.getParent();

			if (parent instanceof IASTDeclaration) {
				if (parent instanceof IASTFunctionDefinition) {
					if (declarator.getName() != null && declarator.getName().resolveBinding() == binding) {
						addName(declarator.getName());
					}
				} else if (parent instanceof IASTSimpleDeclaration) {
					// prototype parameter with no identifier isn't a declaration of the K&R C parameter
					//					if (binding instanceof CKnRParameter && declarator.getName().toCharArray().length == 0)
					//						return PROCESS_CONTINUE;

					if ((declarator.getName() != null && declarator.getName().resolveBinding() == binding)) {
						addName(declarator.getName());
					}
				}
			} else if (parent instanceof IASTParameterDeclaration) {
				if (declarator.getName() != null && declarator.getName().resolveBinding() == binding) {
					addName(declarator.getName());
				}
			}

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processDeclSpecifier(org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier)
		 */
		@Override
		public int visit(IASTDeclSpecifier declSpec) {
			if (compositeTypeDeclared && declSpec instanceof ICASTTypedefNameSpecifier)
				return PROCESS_CONTINUE;

			//if the binding isn't declared in a decl spec, skip it
			if (!(binding instanceof ICompositeType) && !(binding instanceof IEnumeration))
				return PROCESS_CONTINUE;

			if (binding instanceof ICompositeType && declSpec instanceof IASTCompositeTypeSpecifier) {
				if (((IASTCompositeTypeSpecifier) declSpec).getName().resolveBinding() == binding) {
					compositeTypeDeclared = true;
					addName(((IASTCompositeTypeSpecifier) declSpec).getName());
				}
			} else if (binding instanceof IEnumeration && declSpec instanceof IASTEnumerationSpecifier) {
				if (((IASTEnumerationSpecifier) declSpec).getName().resolveBinding() == binding) {
					compositeTypeDeclared = true;
					addName(((IASTEnumerationSpecifier) declSpec).getName());
				}
			} else if (declSpec instanceof IASTElaboratedTypeSpecifier) {
				if (compositeTypeDeclared) {
					IASTNode parent = declSpec.getParent();
					if (!(parent instanceof IASTSimpleDeclaration)
							|| ((IASTSimpleDeclaration) parent).getDeclarators().length > 0) {
						return PROCESS_CONTINUE;
					}
				}
				if (((IASTElaboratedTypeSpecifier) declSpec).getName().resolveBinding() == binding) {
					compositeTypeDeclared = true;
					addName(((IASTElaboratedTypeSpecifier) declSpec).getName());
				}
			}

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processEnumerator(org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator)
		 */
		@Override
		public int visit(IASTEnumerator enumerator) {
			if (binding instanceof IEnumerator && enumerator.getName().resolveBinding() == binding) {
				addName(enumerator.getName());
			}

			return PROCESS_CONTINUE;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.core.dom.parser.c.CVisitor.CBaseVisitorAction#processStatement(org.eclipse.cdt.core.dom.ast.IASTStatement)
		 */
		@Override
		public int visit(IASTStatement statement) {
			if (statement instanceof IASTLabelStatement && binding instanceof ILabel) {
				if (((IASTLabelStatement) statement).getName().resolveBinding() == binding)
					addName(((IASTLabelStatement) statement).getName());
				return PROCESS_SKIP;
			}

			return PROCESS_CONTINUE;
		}
	}

	public static class CollectReferencesAction extends ASTVisitor {
		private static final int DEFAULT_LIST_SIZE = 8;
		private IASTName[] refs;
		private final IBinding binding;
		private int idx = 0;
		private int kind;

		private static final int KIND_LABEL = 1;
		private static final int KIND_OBJ_FN = 2;
		private static final int KIND_TYPE = 3;

		public CollectReferencesAction(IBinding binding) {
			this.binding = binding;
			this.refs = new IASTName[DEFAULT_LIST_SIZE];

			shouldVisitNames = true;
			if (binding instanceof ILabel) {
				kind = KIND_LABEL;
			} else if (binding instanceof ICompositeType || binding instanceof ITypedef
					|| binding instanceof IEnumeration) {
				kind = KIND_TYPE;
			} else {
				kind = KIND_OBJ_FN;
			}
		}

		@Override
		public int visit(IASTName name) {
			ASTNodeProperty prop = name.getPropertyInParent();
			switch (kind) {
			case KIND_LABEL:
				if (prop == IASTGotoStatement.NAME)
					break;
				return PROCESS_CONTINUE;
			case KIND_TYPE:
				if (prop == IASTNamedTypeSpecifier.NAME) {
					break;
				} else if (prop == IASTElaboratedTypeSpecifier.TYPE_NAME) {
					IASTNode p = name.getParent().getParent();
					if (!(p instanceof IASTSimpleDeclaration)
							|| ((IASTSimpleDeclaration) p).getDeclarators().length > 0) {
						break;
					}
				}
				return PROCESS_CONTINUE;
			case KIND_OBJ_FN:
				if (prop == IASTIdExpression.ID_NAME || prop == IASTFieldReference.FIELD_NAME
						|| prop == ICASTFieldDesignator.FIELD_NAME) {
					break;
				}
				return PROCESS_CONTINUE;
			}

			if (CharArrayUtils.equals(name.toCharArray(), binding.getNameCharArray())) {
				if (sameBinding(name.resolveBinding(), binding)) {
					if (refs.length == idx) {
						IASTName[] temp = new IASTName[refs.length * 2];
						System.arraycopy(refs, 0, temp, 0, refs.length);
						refs = temp;
					}
					refs[idx++] = name;
				}
			}
			return PROCESS_CONTINUE;
		}

		private boolean sameBinding(IBinding binding1, IBinding binding2) {
			if (binding1 == binding2)
				return true;
			if (binding1 != null && binding1.equals(binding2))
				return true;
			return false;
		}

		public IASTName[] getReferences() {
			if (idx < refs.length) {
				IASTName[] temp = new IASTName[idx];
				System.arraycopy(refs, 0, temp, 0, idx);
				refs = temp;
			}
			return refs;
		}
	}

	protected static final ASTNodeProperty STRING_LOOKUP_PROPERTY = new ASTNodeProperty(
			"CVisitor.STRING_LOOKUP_PROPERTY - STRING_LOOKUP"); //$NON-NLS-1$
	protected static final ASTNodeProperty STRING_LOOKUP_TAGS_PROPERTY = new ASTNodeProperty(
			"CVisitor.STRING_LOOKUP_TAGS_PROPERTY - STRING_LOOKUP"); //$NON-NLS-1$
	private static final String SIZE_T = "size_t"; //$NON-NLS-1$
	private static final String PTRDIFF_T = "ptrdiff_t"; //$NON-NLS-1$
	public static final String EMPTY_STRING = ""; //$NON-NLS-1$
	// Definition lookup start location
	protected static final int AT_BEGINNING = 1;
	protected static final int AT_NEXT = 2;

	static protected void createBinding(IASTName name) {
		IBinding binding = null;
		IASTNode parent = name.getParent();

		if (parent instanceof CASTIdExpression) {
			binding = resolveBinding(parent);
		} else if (parent instanceof ICASTTypedefNameSpecifier) {
			binding = resolveBinding(parent);
		} else if (parent instanceof IASTFieldReference) {
			binding = (IBinding) findBinding((IASTFieldReference) parent, false);
			if (binding == null) {
				binding = new ProblemBinding(name, IProblemBinding.SEMANTIC_NAME_NOT_FOUND, name.toCharArray());
			}
		} else if (parent instanceof IASTDeclarator) {
			binding = createBinding((IASTDeclarator) parent, name);
		} else if (parent instanceof ICASTCompositeTypeSpecifier) {
			binding = createBinding((ICASTCompositeTypeSpecifier) parent);
		} else if (parent instanceof ICASTElaboratedTypeSpecifier) {
			binding = createBinding((ICASTElaboratedTypeSpecifier) parent);
		} else if (parent instanceof IASTGotoStatement) {
			binding = resolveBinding((IASTGotoStatement) parent);
		} else if (parent instanceof IASTLabelStatement) {
			binding = createBinding((IASTLabelStatement) parent);
		} else if (parent instanceof ICASTEnumerationSpecifier) {
			binding = createBinding((ICASTEnumerationSpecifier) parent);
		} else if (parent instanceof IASTEnumerator) {
			binding = createBinding((IASTEnumerator) parent);
		} else if (parent instanceof ICASTFieldDesignator) {
			binding = resolveBinding(parent);
		}
		name.setBinding(binding);
	}

	private static IBinding createBinding(ICASTEnumerationSpecifier enumeration) {
		IASTName name = enumeration.getName();
		IScope scope = getContainingScope(enumeration);
		IBinding binding = null;
		if (scope != null) {
			binding = scope.getBinding(name, false);
		}
		if (binding != null && !(binding instanceof IIndexBinding) && name.isActive()) {
			if (binding instanceof IEnumeration) {
				if (binding instanceof CEnumeration) {
					((CEnumeration) binding).addDefinition(name);
				}
			} else {
				return new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_OVERLOAD, name.toCharArray());
			}
		} else {
			binding = new CEnumeration(name);
			ASTInternal.addName(scope, name);
		}
		return binding;
	}

	private static IBinding createBinding(IASTEnumerator enumerator) {
		IEnumerator binding = new CEnumerator(enumerator);
		try {
			ASTInternal.addName(binding.getScope(), enumerator.getName());
		} catch (DOMException e) {
		}
		return binding;
	}

	private static IBinding resolveBinding(IASTGotoStatement statement) {
		return resolveLabel(statement.getName());
	}

	private static IBinding createBinding(IASTLabelStatement statement) {
		IASTName name = statement.getName();
		IBinding binding = new CLabel(name);
		try {
			IScope scope = binding.getScope();
			if (scope instanceof ICFunctionScope)
				ASTInternal.addName(binding.getScope(), name);
		} catch (DOMException e) {
		}
		return binding;
	}

	private static IBinding createBinding(ICASTElaboratedTypeSpecifier elabTypeSpec) {
		IASTNode parent = elabTypeSpec.getParent();
		IASTName name = elabTypeSpec.getName();
		if (parent instanceof IASTDeclaration) {
			IBinding binding = null;
			IScope insertIntoScope = null;
			if (parent instanceof IASTSimpleDeclaration
					&& ((IASTSimpleDeclaration) parent).getDeclarators().length == 0) {
				IScope scope = getContainingScope(elabTypeSpec);
				try {
					while (scope instanceof ICCompositeTypeScope)
						scope = scope.getParent();

					binding = scope.getBinding(name, false);
				} catch (DOMException e) {
				}
				if (binding != null && name.isActive()) {
					if (binding instanceof CEnumeration) {
						((CEnumeration) binding).addDeclaration(name);
					} else if (binding instanceof CStructure) {
						((CStructure) binding).addDeclaration(name);
					}
				}
			} else {
				binding = resolveBinding(elabTypeSpec);
				if (binding == null) {
					insertIntoScope = elabTypeSpec.getTranslationUnit().getScope();
					binding = insertIntoScope.getBinding(name, false);
					if (binding != null && name.isActive()) {
						if (binding instanceof CEnumeration) {
							((CEnumeration) binding).addDeclaration(name);
						} else if (binding instanceof CStructure) {
							((CStructure) binding).addDeclaration(name);
						}
					}
				}
			}
			if (binding == null) {
				if (elabTypeSpec.getKind() == IASTElaboratedTypeSpecifier.k_enum) {
					binding = new CEnumeration(name);
				} else {
					binding = new CStructure(name);
				}
				if (insertIntoScope != null) {
					ASTInternal.addName(insertIntoScope, name);
				}
			}
			return binding;
		} else if (parent instanceof IASTTypeId || parent instanceof IASTParameterDeclaration) {
			return resolveBinding(elabTypeSpec);
		}
		return null;
	}

	/**
	 * if prefix == false, return an IBinding or null
	 * if prefix == true, return an IBinding[] or null
	 * @param fieldReference
	 * @param prefix
	 * @return
	 */
	private static Object findBinding(IASTFieldReference fieldReference, boolean prefix) {
		IASTExpression fieldOwner = fieldReference.getFieldOwner();
		if (fieldOwner == null)
			return null;

		boolean isPointerDeref = fieldReference.isPointerDereference();
		IType type = fieldOwner.getExpressionType();
		while (type != null && type instanceof ITypeContainer) {
			if (type instanceof IPointerType) {
				if (isPointerDeref) {
					isPointerDeref = false;
				} else {
					return null;
				}
			}
			type = ((ITypeContainer) type).getType();
		}

		if (type != null && type instanceof ICompositeType) {
			ICompositeType ct = (ICompositeType) type;
			if (ct instanceof IIndexBinding) {
				ct = ((CASTTranslationUnit) fieldReference.getTranslationUnit()).mapToASTType(ct);
			}
			if (prefix) {
				char[] p = fieldReference.getFieldName().toCharArray();
				return findFieldsByPrefix(ct, p);
			}
			return ct.findField(fieldReference.getFieldName().toString());
		}
		return null;
	}

	public static IBinding[] findFieldsByPrefix(final ICompositeType ct, char[] p) {
		IBinding[] result = null;
		IContentAssistMatcher matcher = ContentAssistMatcherFactory.getInstance().createMatcher(p);
		IField[] fields = ct.getFields();
		for (IField field : fields) {
			if (matcher.match(field.getNameCharArray())) {
				result = ArrayUtil.append(IBinding.class, result, field);
			}
		}
		return ArrayUtil.trim(IBinding.class, result);
	}

	static IType getPtrDiffType(IASTBinaryExpression expr) {
		IScope scope = getContainingScope(expr);
		IBinding[] bs = scope.find(PTRDIFF_T, expr.getTranslationUnit());
		for (IBinding b : bs) {
			if (b instanceof IType) {
				if (!(b instanceof ICInternalBinding)
						|| CVisitor.declaredBefore(((ICInternalBinding) b).getPhysicalNode(), expr)) {
					return (IType) b;
				}
			}
		}

		return new CBasicType(Kind.eInt, IBasicType.IS_LONG, expr);
	}

	static IType get_SIZE_T(IASTExpression expr) {
		IASTTranslationUnit tu = expr.getTranslationUnit();
		if (tu != null) {
			IBinding[] bs = tu.getScope().find(SIZE_T, expr.getTranslationUnit());
			for (IBinding b : bs) {
				if (b instanceof IType) {
					if (!(b instanceof ICInternalBinding)
							|| CVisitor.declaredBefore(((ICInternalBinding) b).getPhysicalNode(), expr)) {
						return (IType) b;
					}
				}
			}
		}
		return UNSIGNED_LONG_INT;
	}

	static IType unwrapTypedefs(IType type) {
		while (type instanceof ITypedef) {
			type = ((ITypedef) type).getType();
		}
		return type;
	}

	static IType unwrapCV(IType type) {
		while (type instanceof IQualifierType) {
			type = ((IQualifierType) type).getType();
		}
		if (type instanceof IPointerType) {
			type = new CPointerType(((IPointerType) type).getType(), 0 /* no qualifiers */);
		}
		return type;
	}

	/**
	 * @param parent
	 * @return
	 */
	private static IBinding createBinding(IASTDeclarator declarator, IASTName name) {
		IBinding binding = null;
		if (declarator instanceof ICASTKnRFunctionDeclarator) {
			if (CharArrayUtils.equals(declarator.getName().toCharArray(), name.toCharArray())) {
				IScope scope = CVisitor.getContainingScope(declarator);
				binding = scope.getBinding(name, false);
				if (binding != null && !(binding instanceof IIndexBinding) && name.isActive()) {
					if (binding instanceof ICInternalFunction) {
						((ICInternalFunction) binding).addDeclarator(declarator);
					} else {
						binding = new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_OVERLOAD,
								name.toCharArray());
					}
				} else {
					binding = createBinding(declarator);
				}
			} else { // createBinding for one of the ICASTKnRFunctionDeclarator's parameterNames
				IBinding f = declarator.getName().resolveBinding();
				if (f instanceof CFunction) {
					binding = ((CFunction) f).resolveParameter(name);
				}
			}
		} else {
			binding = createBinding(declarator);
		}
		return binding;
	}

	private static IBinding createBinding(IASTDeclarator declarator) {
		IASTNode parent = ASTQueries.findOutermostDeclarator(declarator).getParent();
		declarator = ASTQueries.findInnermostDeclarator(declarator);
		IASTDeclarator typeRelevant = ASTQueries.findTypeRelevantDeclarator(declarator);

		IScope scope = getContainingScope(parent);
		ASTNodeProperty prop = parent.getPropertyInParent();
		if (prop == IASTDeclarationStatement.DECLARATION) {
			//implicit scope, see 6.8.4-3
			prop = parent.getParent().getPropertyInParent();
			if (prop != IASTCompoundStatement.NESTED_STATEMENT)
				scope = null;
		}

		IASTName name = declarator.getName();

		IBinding binding = (scope != null) ? scope.getBinding(name, false) : null;

		boolean isFunction = false;
		if (parent instanceof IASTParameterDeclaration
				|| parent.getPropertyInParent() == ICASTKnRFunctionDeclarator.FUNCTION_PARAMETER) {
			IASTDeclarator fdtor = (IASTDeclarator) parent.getParent();
			// Create parameter bindings only if the declarator declares a function
			if (ASTQueries.findTypeRelevantDeclarator(fdtor) == fdtor) {
				IASTName n = ASTQueries.findInnermostDeclarator(fdtor).getName();
				IBinding temp = n.resolveBinding();
				if (temp != null && temp instanceof CFunction) {
					binding = ((CFunction) temp).resolveParameter(name);
				} else if (temp instanceof IFunction) {
					//problems with the function, still create binding for the parameter
					binding = new CParameter(name);
				}
				return binding;
			}
		} else if (parent instanceof IASTFunctionDefinition) {
			isFunction = true;
		} else if (parent instanceof IASTSimpleDeclaration) {
			IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) parent;
			if (simpleDecl.getDeclSpecifier().getStorageClass() == IASTDeclSpecifier.sc_typedef) {
				binding = new CTypedef(name);
			} else {
				isFunction = typeRelevant instanceof IASTFunctionDeclarator;
				if (!isFunction) {
					IType t1 = createType(declarator), t2 = null;
					if (CVisitor.unwrapTypedefs(t1) instanceof IFunctionType) {
						isFunction = true;
					} else {
						if (binding != null && !(binding instanceof IIndexBinding) && name.isActive()) {
							if (binding instanceof IParameter) {
								return new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_REDECLARATION,
										name.toCharArray());
							} else if (binding instanceof IVariable) {
								t2 = ((IVariable) binding).getType();
								if (t1 != null && t2 != null
										&& (areArraysOfTheSameElementType(t1, t2) || t1.isSameType(t2))) {
									if (binding instanceof CVariable)
										((CVariable) binding).addDeclaration(name);
								} else {
									return new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_REDECLARATION,
											name.toCharArray());
								}
							}
						} else if (simpleDecl.getParent() instanceof ICASTCompositeTypeSpecifier) {
							binding = new CField(name);
						} else {
							binding = new CVariable(name);
						}
					}
				}
			}
		}
		if (isFunction) {
			if (binding != null && !(binding instanceof IIndexBinding) && name.isActive()) {
				if (binding instanceof IFunction) {
					IFunction function = (IFunction) binding;
					if (function instanceof CFunction) {
						((CFunction) function).addDeclarator(typeRelevant);
					}
					return function;
				}
				binding = new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_OVERLOAD, name.toCharArray());
			} else {
				binding = new CFunction(typeRelevant);
			}

		}
		return binding;
	}

	private static IBinding createBinding(ICASTCompositeTypeSpecifier compositeTypeSpec) {
		IScope scope = null;
		IBinding binding = null;
		IASTName name = compositeTypeSpec.getName();
		try {
			scope = getContainingScope(compositeTypeSpec);
			while (scope instanceof ICCompositeTypeScope)
				scope = scope.getParent();

			if (scope != null) {
				binding = scope.getBinding(name, false);
				if (binding != null && !(binding instanceof IIndexBinding) && name.isActive()) {
					if (binding instanceof CStructure)
						((CStructure) binding).addDefinition(compositeTypeSpec);
					return binding;
				}
			}
		} catch (DOMException e) {
		}
		return new CStructure(name);
	}

	protected static IBinding resolveBinding(IASTNode node) {
		if (node instanceof IASTFunctionDefinition) {
			IASTFunctionDefinition functionDef = (IASTFunctionDefinition) node;
			IASTFunctionDeclarator functionDeclartor = functionDef.getDeclarator();
			IASTName name = findInnermostDeclarator(functionDeclartor).getName();
			IScope scope = getContainingScope(node);
			return lookup(scope, name);
		} else if (node instanceof IASTIdExpression) {
			if (isLabelReference(node)) {
				return resolveLabel(((IASTIdExpression) node).getName());
			}
			IScope scope = getContainingScope(node);
			IBinding binding = lookup(scope, ((IASTIdExpression) node).getName());
			if (binding instanceof IType && !(binding instanceof IProblemBinding)) {
				return new ProblemBinding(node, IProblemBinding.SEMANTIC_INVALID_TYPE, binding.getNameCharArray(),
						new IBinding[] { binding });
			}
			return binding;
		} else if (node instanceof ICASTTypedefNameSpecifier) {
			IScope scope = getContainingScope(node);
			IASTName name = ((ICASTTypedefNameSpecifier) node).getName();
			IBinding binding = lookup(scope, name);
			if (binding == null)
				return new ProblemBinding(node, IProblemBinding.SEMANTIC_NAME_NOT_FOUND, name.toCharArray());
			if (binding instanceof IType)
				return binding;
			return new ProblemBinding(node, IProblemBinding.SEMANTIC_INVALID_TYPE, binding.getNameCharArray(),
					new IBinding[] { binding });
		} else if (node instanceof ICASTElaboratedTypeSpecifier) {
			IScope scope = getContainingScope(node);
			return lookup(scope, ((ICASTElaboratedTypeSpecifier) node).getName());
		} else if (node instanceof ICASTCompositeTypeSpecifier) {
			IScope scope = getContainingScope(node);
			return lookup(scope, ((ICASTCompositeTypeSpecifier) node).getName());
		} else if (node instanceof IASTTypeId) {
			IASTTypeId typeId = (IASTTypeId) node;
			IASTDeclSpecifier declSpec = typeId.getDeclSpecifier();
			IASTName name = null;
			if (declSpec instanceof ICASTElaboratedTypeSpecifier) {
				name = ((ICASTElaboratedTypeSpecifier) declSpec).getName();
			} else if (declSpec instanceof ICASTCompositeTypeSpecifier) {
				name = ((ICASTCompositeTypeSpecifier) declSpec).getName();
			} else if (declSpec instanceof ICASTTypedefNameSpecifier) {
				name = ((ICASTTypedefNameSpecifier) declSpec).getName();
			}
			if (name != null) {
				IBinding binding = name.resolveBinding();
				if (binding instanceof IType) {
					return binding;
				} else if (binding != null) {
					return new ProblemBinding(node, IProblemBinding.SEMANTIC_INVALID_TYPE, binding.getNameCharArray(),
							new IBinding[] { binding });
				}
				return null;
			}
		} else if (node instanceof ICASTFieldDesignator) {
			IASTNode blockItem = getContainingBlockItem(node);

			if ((blockItem instanceof IASTSimpleDeclaration || (blockItem instanceof IASTDeclarationStatement
					&& ((IASTDeclarationStatement) blockItem).getDeclaration() instanceof IASTSimpleDeclaration))) {
				IASTSimpleDeclaration simpleDecl = null;
				if (blockItem instanceof IASTDeclarationStatement
						&& ((IASTDeclarationStatement) blockItem).getDeclaration() instanceof IASTSimpleDeclaration) {
					simpleDecl = (IASTSimpleDeclaration) ((IASTDeclarationStatement) blockItem).getDeclaration();
				} else if (blockItem instanceof IASTSimpleDeclaration) {
					simpleDecl = (IASTSimpleDeclaration) blockItem;
				}

				if (simpleDecl != null) {
					IBinding struct = null;
					if (simpleDecl.getDeclSpecifier() instanceof IASTNamedTypeSpecifier)
						struct = ((IASTNamedTypeSpecifier) simpleDecl.getDeclSpecifier()).getName().resolveBinding();
					else if (simpleDecl.getDeclSpecifier() instanceof IASTElaboratedTypeSpecifier)
						struct = ((IASTElaboratedTypeSpecifier) simpleDecl.getDeclSpecifier()).getName()
								.resolveBinding();
					else if (simpleDecl.getDeclSpecifier() instanceof IASTCompositeTypeSpecifier)
						struct = ((IASTCompositeTypeSpecifier) simpleDecl.getDeclSpecifier()).getName()
								.resolveBinding();

					if (struct instanceof ICompositeType) {
						return ((ICompositeType) struct).findField(((ICASTFieldDesignator) node).getName().toString());
					} else if (struct instanceof ITypeContainer) {
						IType type;
						type = ((ITypeContainer) struct).getType();
						while (type instanceof ITypeContainer && !(type instanceof CStructure)) {
							type = ((ITypeContainer) type).getType();
						}

						if (type instanceof CStructure)
							return ((CStructure) type).findField(((ICASTFieldDesignator) node).getName().toString());
					}
				}
			}
		}
		return null;
	}

	/**
	 * May return <code>null</code>, e.g. for parameter names in function-prototypes.
	 */
	public static IScope getContainingScope(IASTNode node) {
		if (node == null)
			return null;
		while (node != null) {
			if (node instanceof IASTDeclaration) {
				IASTNode parent = node.getParent();
				if (parent instanceof IASTTranslationUnit) {
					return ((IASTTranslationUnit) parent).getScope();
				} else if (parent instanceof IASTDeclarationStatement) {
					return getContainingScope((IASTStatement) parent);
				} else if (parent instanceof IASTForStatement) {
					return ((IASTForStatement) parent).getScope();
				} else if (parent instanceof IASTCompositeTypeSpecifier) {
					return ((IASTCompositeTypeSpecifier) parent).getScope();
				} else if (parent instanceof ICASTKnRFunctionDeclarator) {
					parent = ((IASTDeclarator) parent).getParent();
					if (parent instanceof IASTFunctionDefinition) {
						return ((IASTCompoundStatement) ((IASTFunctionDefinition) parent).getBody()).getScope();
					}
				}
			} else if (node instanceof IASTStatement) {
				return getContainingScope((IASTStatement) node);
			} else if (node instanceof IASTExpression) {
				IASTNode parent = node.getParent();
				if (parent instanceof IASTForStatement) {
					return ((IASTForStatement) parent).getScope();
				}
			} else if (node instanceof IASTParameterDeclaration) {
				IASTNode parent = node.getParent();
				if (parent instanceof IASTStandardFunctionDeclarator) {
					IASTStandardFunctionDeclarator dtor = (IASTStandardFunctionDeclarator) parent;
					if (ASTQueries.findTypeRelevantDeclarator(dtor) == dtor) {
						parent = ASTQueries.findOutermostDeclarator(dtor);
						ASTNodeProperty prop = parent.getPropertyInParent();
						if (prop == IASTSimpleDeclaration.DECLARATOR)
							return dtor.getFunctionScope();
						else if (prop == IASTFunctionDefinition.DECLARATOR)
							return ((IASTCompoundStatement) ((IASTFunctionDefinition) parent.getParent()).getBody())
									.getScope();
					}
				}
			} else if (node instanceof IASTEnumerator) {
				//put the enumerators in the same scope as the enumeration
				node = node.getParent();
			} else if (node instanceof IASTName) {
				ASTNodeProperty prop = node.getPropertyInParent();
				if (prop == IASTLabelStatement.NAME) {
					IScope scope = getContainingScope(node.getParent());
					//labels have function scope
					while (scope != null && !(scope instanceof ICFunctionScope)) {
						try {
							scope = scope.getParent();
						} catch (DOMException e) {
							scope = e.getProblem();
							break;
						}
					}
					return scope;
				}
			}

			node = node.getParent();
		}
		return null;
	}

	public static IScope getContainingScope(IASTStatement statement) {
		IASTNode parent = statement.getParent();
		IScope scope = null;
		if (parent instanceof IASTCompoundStatement) {
			IASTCompoundStatement compound = (IASTCompoundStatement) parent;
			scope = compound.getScope();
		} else if (parent instanceof IASTStatement) {
			if (parent instanceof IASTForStatement) {
				scope = ((IASTForStatement) parent).getScope();
			} else {
				scope = getContainingScope((IASTStatement) parent);
			}
		} else if (parent instanceof IASTFunctionDefinition) {
			return ((IASTFunctionDefinition) parent).getScope();
		} else {
			return getContainingScope(parent);
		}

		if (statement instanceof IASTGotoStatement) {
			// labels have function scope
			while (scope != null && !(scope instanceof ICFunctionScope)) {
				try {
					scope = scope.getParent();
				} catch (DOMException e) {
					scope = e.getProblem();
					break;
				}
			}
		}

		return scope;
	}

	private static IASTNode getContainingBlockItem(IASTNode node) {
		for (IASTNode parent = node.getParent(); parent != null; parent = parent.getParent()) {
			if (parent instanceof IASTDeclaration) {
				IASTNode p = parent.getParent();
				if (p instanceof IASTDeclarationStatement)
					return p;
				return parent;
			}
			if (parent instanceof IASTCompoundStatement || // parent is something that can contain a declaration
					parent instanceof IASTTranslationUnit || parent instanceof IASTForStatement
					|| parent instanceof IASTFunctionDeclarator) {
				return node;
			}
			node = parent;
		}
		return null;
	}

	/**
	 * Lookup for a name starting from the given scope.
	 */
	protected static IBinding lookup(IScope scope, IASTName name) {
		if (scope == null)
			return null;

		IIndexFileSet fileSet = IIndexFileSet.EMPTY;
		IASTTranslationUnit tu = name.getTranslationUnit();
		if (tu == null && scope instanceof IASTInternalScope) {
			tu = ((IASTInternalScope) scope).getPhysicalNode().getTranslationUnit();
		}
		if (tu != null) {
			final IIndexFileSet fs = tu.getAdapter(IIndexFileSet.class);
			if (fs != null) {
				fileSet = fs;
			}
		}

		while (scope != null) {
			if (!(scope instanceof ICCompositeTypeScope)) {
				IBinding binding = scope.getBinding(name, true, fileSet);
				if (binding != null)
					return binding;
			}
			try {
				scope = scope.getParent();
			} catch (DOMException e) {
				scope = null;
			}
		}

		return externalBinding(tu, name);
	}

	/**
	 * if (bits & PREFIX_LOOKUP) then returns IBinding[]
	 * otherwise returns IBinding
	 */
	protected static IBinding[] lookupPrefix(IScope scope, IASTName name) throws DOMException {
		if (scope == null)
			return null;

		IBinding[] result = null;
		CharArraySet handled = new CharArraySet(1);
		while (scope != null) {
			if (!(scope instanceof ICCompositeTypeScope)) {
				IBinding[] bindings = scope.getBindings(new ScopeLookupData(name, true, true));
				for (IBinding b : bindings) {
					final char[] n = b.getNameCharArray();
					// consider binding only if no binding with the same name was found in another scope.
					if (!handled.containsKey(n)) {
						result = ArrayUtil.append(IBinding.class, result, b);
					}
				}
				// store names of bindings
				for (IBinding b : bindings) {
					final char[] n = b.getNameCharArray();
					handled.put(n);
				}
			}
			scope = scope.getParent();
		}

		return ArrayUtil.trim(IBinding.class, result);
	}

	private static IBinding externalBinding(IASTTranslationUnit tu, IASTName name) {
		IASTNode parent = name.getParent();
		IBinding external = null;
		if (parent instanceof IASTIdExpression) {
			if (parent.getPropertyInParent() == IASTFunctionCallExpression.FUNCTION_NAME) {
				//external function
				external = new CExternalFunction(tu, name);
				ASTInternal.addName(tu.getScope(), name);
			} else {
				//external variable
				//external = new CExternalVariable(tu, name);
				//((CScope) tu.getScope()).addName(name);
				external = new ProblemBinding(name, IProblemBinding.SEMANTIC_NAME_NOT_FOUND, name.toCharArray());
			}
		}
		return external;
	}

	protected static IASTDeclarator findDefinition(IASTDeclarator declarator, int beginAtLoc) {
		return (IASTDeclarator) findDefinition(declarator, declarator.getName().toCharArray(), beginAtLoc);
	}

	protected static IASTFunctionDeclarator findDefinition(IASTFunctionDeclarator declarator) {
		return (IASTFunctionDeclarator) findDefinition(declarator, declarator.getName().toCharArray(), AT_NEXT);
	}

	protected static IASTDeclSpecifier findDefinition(ICASTElaboratedTypeSpecifier declSpec) {
		return (IASTDeclSpecifier) findDefinition(declSpec, declSpec.getName().toCharArray(), AT_BEGINNING);
	}

	private static IASTNode findDefinition(IASTNode decl, char[] declName, int beginAtLoc) {
		IASTNode blockItem = getContainingBlockItem(decl);
		IASTNode parent = blockItem.getParent();
		if (parent instanceof IASTCompositeTypeSpecifier) {
			// C does not have a notion of structure scope.
			// Types names declared in a structure have file scope.
			parent = parent.getParent(); // This will be the declaration
			if (parent != null) {
				parent = parent.getParent();
			}
		}
		IASTNode[] list = null;
		if (parent instanceof IASTCompoundStatement) {
			IASTCompoundStatement compound = (IASTCompoundStatement) parent;
			list = compound.getStatements();
		} else if (parent instanceof IASTTranslationUnit) {
			IASTTranslationUnit translation = (IASTTranslationUnit) parent;
			list = translation.getDeclarations();
		}
		boolean begun = (beginAtLoc == AT_BEGINNING);
		if (list != null) {
			for (IASTNode node : list) {
				if (node == blockItem) {
					begun = true;
					continue;
				}

				if (begun) {
					if (node instanceof IASTDeclarationStatement) {
						node = ((IASTDeclarationStatement) node).getDeclaration();
					}

					if (node instanceof IASTFunctionDefinition && decl instanceof IASTFunctionDeclarator) {
						IASTFunctionDeclarator dtor = ((IASTFunctionDefinition) node).getDeclarator();
						IASTName name = ASTQueries.findInnermostDeclarator(dtor).getName();
						if (name.toString().equals(declName)) {
							return dtor;
						}
					} else if (node instanceof IASTSimpleDeclaration && decl instanceof ICASTElaboratedTypeSpecifier) {
						IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node;
						IASTDeclSpecifier declSpec = simpleDecl.getDeclSpecifier();
						IASTName name = null;

						if (declSpec instanceof ICASTCompositeTypeSpecifier) {
							name = ((ICASTCompositeTypeSpecifier) declSpec).getName();
						} else if (declSpec instanceof ICASTEnumerationSpecifier) {
							name = ((ICASTEnumerationSpecifier) declSpec).getName();
						}
						if (name != null) {
							if (CharArrayUtils.equals(name.toCharArray(), declName)) {
								return declSpec;
							}
						}
					} else if (node instanceof IASTSimpleDeclaration && decl instanceof IASTDeclarator) {
						IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node;
						IASTDeclarator[] dtors = simpleDecl.getDeclarators();
						for (int j = 0; dtors != null && j < dtors.length; j++) {
							if (CharArrayUtils.equals(dtors[j].getName().toCharArray(), declName)) {
								return dtors[j];
							}
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * Create an IType for an IASTDeclarator.
	 *
	 * @param declarator the IASTDeclarator whose IType will be created
	 * @return the IType of the IASTDeclarator parameter
	 */
	public static IType createType(IASTDeclarator declarator) {
		return createType(declarator, 0);
	}

	public static IType createType(IASTDeclarator declarator, int flags) {
		IASTDeclSpecifier declSpec = null;

		IASTNode node = declarator.getParent();
		while (node instanceof IASTDeclarator) {
			declarator = (IASTDeclarator) node;
			node = node.getParent();
		}

		if (node instanceof IASTSimpleDeclaration) {
			declSpec = ((IASTSimpleDeclaration) node).getDeclSpecifier();
		} else if (node instanceof IASTParameterDeclaration) {
			declSpec = ((IASTParameterDeclaration) node).getDeclSpecifier();
		} else if (node instanceof IASTFunctionDefinition) {
			declSpec = ((IASTFunctionDefinition) node).getDeclSpecifier();
		} else if (node instanceof IASTTypeId) {
			declSpec = ((IASTTypeId) node).getDeclSpecifier();
		}

		boolean isParameter = (node instanceof IASTParameterDeclaration
				|| node.getParent() instanceof ICASTKnRFunctionDeclarator);

		IType type = createType((ICASTDeclSpecifier) declSpec);
		type = createType(type, declarator, flags);

		if (isParameter) {
			IType paramType = type;
			// Remove typedefs ready for subsequent processing.
			while (paramType instanceof ITypedef) {
				paramType = ((ITypedef) paramType).getType();
			}

			//C99: 6.7.5.3-7 a declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the
			//type qualifiers (if any) are those specified within the[and] of the array type derivation
			if (paramType instanceof IArrayType) { // the index does not yet return ICArrayTypes
				IArrayType at = (IArrayType) paramType;
				int q = 0;
				if (at instanceof ICArrayType) {
					ICArrayType cat = (ICArrayType) at;
					if (cat.isConst())
						q |= CPointerType.IS_CONST;
					if (cat.isVolatile())
						q |= CPointerType.IS_VOLATILE;
					if (cat.isRestrict())
						q |= CPointerType.IS_RESTRICT;
				}
				type = new CPointerType(at.getType(), q);
			} else if (paramType instanceof IFunctionType) {
				//-8 A declaration of a parameter as "function returning type" shall be adjusted to "pointer to function returning type"
				type = new CPointerType(paramType, 0);
			}
		}

		return type;
	}

	private static IType createType(IASTTypeId typeId) {
		return createType(typeId.getAbstractDeclarator());
	}

	public static IType createType(IType baseType, IASTDeclarator declarator, int flags) {
		if (((flags & ONLY_RETURN_TYPE) == 0) && declarator instanceof IASTFunctionDeclarator)
			return createType(baseType, (IASTFunctionDeclarator) declarator);

		IType type = baseType;
		type = applyAttributes(type, declarator);
		type = setupPointerChain(declarator.getPointerOperators(), type);
		type = setupArrayChain(declarator, type);

		IASTDeclarator nested = declarator.getNestedDeclarator();
		if (nested != null) {
			return createType(type, nested, flags);
		}
		return type;
	}

	private static IType applyAttributes(IType type, IASTDeclarator declarator) {
		if (type instanceof IBasicType) {
			IBasicType basicType = (IBasicType) type;
			if (basicType.getKind() == IBasicType.Kind.eInt) {
				IASTAttribute[] attributes = declarator.getAttributes();
				for (IASTAttribute attribute : attributes) {
					char[] name = attribute.getName();
					if (CharArrayUtils.equals(name, "__mode__") || CharArrayUtils.equals(name, "mode")) { //$NON-NLS-1$ //$NON-NLS-2$
						char[] mode = AttributeUtil.getSimpleArgument(attribute);
						if (CharArrayUtils.equals(mode, "__QI__") || CharArrayUtils.equals(mode, "QI")) { //$NON-NLS-1$ //$NON-NLS-2$
							type = new CBasicType(IBasicType.Kind.eChar,
									basicType.isUnsigned() ? IBasicType.IS_UNSIGNED : IBasicType.IS_SIGNED);
						} else if (CharArrayUtils.equals(mode, "__HI__") || CharArrayUtils.equals(mode, "HI")) { //$NON-NLS-1$ //$NON-NLS-2$
							type = new CBasicType(IBasicType.Kind.eInt,
									IBasicType.IS_SHORT | getSignModifiers(basicType));
						} else if (CharArrayUtils.equals(mode, "__SI__") || CharArrayUtils.equals(mode, "SI")) { //$NON-NLS-1$ //$NON-NLS-2$
							type = new CBasicType(IBasicType.Kind.eInt, getSignModifiers(basicType));
						} else if (CharArrayUtils.equals(mode, "__DI__") || CharArrayUtils.equals(mode, "DI")) { //$NON-NLS-1$ //$NON-NLS-2$
							SizeofCalculator sizeofs = new SizeofCalculator(declarator.getTranslationUnit());
							int modifier;
							if (sizeofs.sizeof_long != null && sizeofs.sizeof_int != null
									&& sizeofs.sizeof_long.size == 2 * sizeofs.sizeof_int.size) {
								modifier = IBasicType.IS_LONG;
							} else {
								modifier = IBasicType.IS_LONG_LONG;
							}
							type = new CBasicType(IBasicType.Kind.eInt, modifier | getSignModifiers(basicType));
						} else if (CharArrayUtils.equals(mode, "__word__") || CharArrayUtils.equals(mode, "word")) { //$NON-NLS-1$ //$NON-NLS-2$
							type = new CBasicType(IBasicType.Kind.eInt,
									IBasicType.IS_LONG | getSignModifiers(basicType));
						}
					}
				}
			}
		}
		return type;
	}

	private static int getSignModifiers(IBasicType type) {
		return type.getModifiers() & (IBasicType.IS_SIGNED | IBasicType.IS_UNSIGNED);
	}

	public static IType createType(IType returnType, IASTFunctionDeclarator declarator) {
		IType[] pTypes = getParmTypes(declarator);
		returnType = setupPointerChain(declarator.getPointerOperators(), returnType);

		boolean takesVarargs = declarator instanceof IASTStandardFunctionDeclarator
				&& ((IASTStandardFunctionDeclarator) declarator).takesVarArgs();
		IType type = new CFunctionType(returnType, pTypes, takesVarargs);

		IASTDeclarator nested = declarator.getNestedDeclarator();
		if (nested != null) {
			return createType(type, nested, 0);
		}
		return type;
	}

	/**
	 * This is used to create a base IType corresponding to an IASTDeclarator and
	 * the IASTDeclSpecifier.  This method doesn't have any recursive behavior and is used as
	 * the foundation of the ITypes being created. The parameter isParm is used to specify whether
	 * the declarator is a parameter or not.
	 *
	 * @param declSpec the IASTDeclSpecifier used to determine if the base type is a CQualifierType
	 * 		or not
	 * @return the base IType
	 */
	public static IType createBaseType(IASTDeclSpecifier declSpec) {
		if (declSpec instanceof ICASTSimpleDeclSpecifier) {
			final ICASTSimpleDeclSpecifier sds = (ICASTSimpleDeclSpecifier) declSpec;
			IASTExpression exp = sds.getDeclTypeExpression();
			if (exp != null)
				return exp.getExpressionType();
			return new CBasicType(sds);
		}
		IBinding binding = null;
		IASTName name = null;
		if (declSpec instanceof ICASTTypedefNameSpecifier) {
			name = ((ICASTTypedefNameSpecifier) declSpec).getName();
		} else if (declSpec instanceof IASTElaboratedTypeSpecifier) {
			name = ((IASTElaboratedTypeSpecifier) declSpec).getName();
		} else if (declSpec instanceof IASTCompositeTypeSpecifier) {
			name = ((IASTCompositeTypeSpecifier) declSpec).getName();
		} else if (declSpec instanceof IASTEnumerationSpecifier) {
			name = ((IASTEnumerationSpecifier) declSpec).getName();
		} else {
			throw new IllegalArgumentException();
		}

		if (name == null)
			return new ProblemType(ISemanticProblem.TYPE_NO_NAME);

		binding = name.resolveBinding();
		if (binding instanceof IType && !(binding instanceof IProblemBinding))
			return (IType) binding;

		return new ProblemType(ISemanticProblem.TYPE_UNRESOLVED_NAME);
	}

	public static IType createType(ICASTDeclSpecifier declSpec) {
		if (declSpec.isConst() || declSpec.isVolatile() || declSpec.isRestrict()) {
			return new CQualifierType(declSpec);
		}

		return createBaseType(declSpec);
	}

	/**
	 * Returns an IType[] corresponding to the parameter types of the IASTFunctionDeclarator parameter.
	 *
	 * @param decltor the IASTFunctionDeclarator to create an IType[] for its parameters
	 * @return IType[] corresponding to the IASTFunctionDeclarator parameters
	 */
	private static IType[] getParmTypes(IASTFunctionDeclarator decltor) {
		if (decltor instanceof IASTStandardFunctionDeclarator) {
			IASTParameterDeclaration parms[] = ((IASTStandardFunctionDeclarator) decltor).getParameters();
			IType parmTypes[] = new IType[parms.length];

			for (int i = 0; i < parms.length; i++) {
				parmTypes[i] = createType(parms[i].getDeclarator());
			}

			if (parmTypes.length == 1 && SemanticUtil.isVoidType(parmTypes[0]))
				return IType.EMPTY_TYPE_ARRAY; // f(void) is the same as f()
			return parmTypes;
		} else if (decltor instanceof ICASTKnRFunctionDeclarator) {
			IASTName parms[] = ((ICASTKnRFunctionDeclarator) decltor).getParameterNames();
			IType parmTypes[] = new IType[parms.length];

			for (int i = 0; i < parms.length; i++) {
				IASTDeclarator dtor = getKnRParameterDeclarator((ICASTKnRFunctionDeclarator) decltor, parms[i]);
				if (dtor != null)
					parmTypes[i] = createType(dtor);
			}
			return parmTypes;
		} else {
			throw new IllegalArgumentException();
		}
	}

	protected static IASTDeclarator getKnRParameterDeclarator(ICASTKnRFunctionDeclarator fKnRDtor, IASTName name) {
		IASTDeclaration[] decls = fKnRDtor.getParameterDeclarations();
		char[] n = name.toCharArray();
		for (int i = 0; i < decls.length; i++) {
			if (!(decls[i] instanceof IASTSimpleDeclaration))
				continue;

			IASTDeclarator[] dtors = ((IASTSimpleDeclaration) decls[i]).getDeclarators();
			for (IASTDeclarator dtor : dtors) {
				if (CharArrayUtils.equals(dtor.getName().toCharArray(), n)) {
					return dtor;
				}
			}
		}
		return null;
	}

	/**
	 * Traverse through an array of IASTArrayModifier[] corresponding to the IASTDeclarator decl parameter.
	 * For each IASTArrayModifier in the array, create a corresponding CArrayType object and
	 * link it in a chain.  The returned IType is the start of the CArrayType chain that represents
	 * the types of the IASTArrayModifier objects in the declarator.
	 *
	 * @param decl the IASTDeclarator containing the IASTArrayModifier[] array to create a CArrayType chain for
	 * @param lastType the IType that the end of the CArrayType chain points to
	 * @return the starting CArrayType at the beginning of the CArrayType chain
	 */
	private static IType setupArrayChain(IASTDeclarator decl, IType lastType) {
		if (decl instanceof IASTArrayDeclarator) {
			IASTArrayModifier[] mods = ((IASTArrayDeclarator) decl).getArrayModifiers();
			for (int i = mods.length - 1; i >= 0; i--) {
				CArrayType arrayType = new CArrayType(lastType);
				if (mods[i] instanceof ICASTArrayModifier) {
					arrayType.setModifier((ICASTArrayModifier) mods[i]);
				}
				lastType = arrayType;
			}
		}

		return lastType;
	}

	/**
	 * Traverse through an array of IASTPointerOperator[] pointers and set up a pointer chain
	 * corresponding to the types of the IASTPointerOperator[].
	 *
	 * @param ptrs an array of IASTPointerOperator[] used to setup the pointer chain
	 * @param lastType the IType that the end of the CPointerType chain points to
	 * @return the starting CPointerType at the beginning of the CPointerType chain
	 */
	private static IType setupPointerChain(IASTPointerOperator[] ptrs, IType lastType) {
		CPointerType pointerType = null;

		if (ptrs != null && ptrs.length > 0) {
			pointerType = new CPointerType();

			if (ptrs.length == 1) {
				pointerType.setType(lastType);
				pointerType.setQualifiers((((ICASTPointer) ptrs[0]).isConst() ? CPointerType.IS_CONST : 0)
						| (((ICASTPointer) ptrs[0]).isRestrict() ? CPointerType.IS_RESTRICT : 0)
						| (((ICASTPointer) ptrs[0]).isVolatile() ? CPointerType.IS_VOLATILE : 0));
			} else {
				CPointerType tempType = new CPointerType();
				pointerType.setType(tempType);
				pointerType.setQualifiers((((ICASTPointer) ptrs[ptrs.length - 1]).isConst() ? CPointerType.IS_CONST : 0)
						| (((ICASTPointer) ptrs[ptrs.length - 1]).isRestrict() ? CPointerType.IS_RESTRICT : 0)
						| (((ICASTPointer) ptrs[ptrs.length - 1]).isVolatile() ? CPointerType.IS_VOLATILE : 0));
				int i = ptrs.length - 2;
				for (; i > 0; i--) {
					tempType.setType(new CPointerType());
					tempType.setQualifiers((((ICASTPointer) ptrs[i]).isConst() ? CPointerType.IS_CONST : 0)
							| (((ICASTPointer) ptrs[i]).isRestrict() ? CPointerType.IS_RESTRICT : 0)
							| (((ICASTPointer) ptrs[i]).isVolatile() ? CPointerType.IS_VOLATILE : 0));
					tempType = (CPointerType) tempType.getType();
				}
				tempType.setType(lastType);
				tempType.setQualifiers((((ICASTPointer) ptrs[i]).isConst() ? CPointerType.IS_CONST : 0)
						| (((ICASTPointer) ptrs[i]).isRestrict() ? CPointerType.IS_RESTRICT : 0)
						| (((ICASTPointer) ptrs[i]).isVolatile() ? CPointerType.IS_VOLATILE : 0));
			}

			return pointerType;
		}

		return lastType;
	}

	public static IASTProblem[] getProblems(IASTTranslationUnit tu) {
		CollectProblemsAction action = new CollectProblemsAction();
		tu.accept(action);

		return action.getProblems();
	}

	public static IASTName[] getDeclarations(IASTTranslationUnit tu, IBinding binding) {
		CollectDeclarationsAction action = new CollectDeclarationsAction(binding);
		tu.accept(action);

		return action.getDeclarationNames();
	}

	public static IASTName[] getReferences(IASTTranslationUnit tu, IBinding binding) {
		CollectReferencesAction action = new CollectReferencesAction(binding);
		tu.accept(action);
		return action.getReferences();
	}

	public static IBinding[] findBindingsForContentAssist(IASTName name, boolean isPrefix) {
		ASTNodeProperty prop = name.getPropertyInParent();

		IBinding[] result = null;

		if (prop == IASTFieldReference.FIELD_NAME) {
			Object res = findBinding((IASTFieldReference) name.getParent(), isPrefix);
			if (isPrefix) {
				result = (IBinding[]) res;
			} else {
				IBinding binding = (IBinding) res;
				if (binding != null) {
					result = new IBinding[] { binding };
				}
			}
		} else if (prop == ICASTFieldDesignator.FIELD_NAME) {
			result = findBindingForContentAssist((ICASTFieldDesignator) name.getParent(), isPrefix);
		} else {
			IScope scope = getContainingScope(name);
			try {
				if (isPrefix) {
					result = lookupPrefix(scope, name);
				} else {
					result = new IBinding[] { lookup(scope, name) };
				}
			} catch (DOMException e) {
			}
		}
		return ArrayUtil.trim(IBinding.class, result);
	}

	private static IBinding[] findBindingForContentAssist(ICASTFieldDesignator fd, boolean isPrefix) {
		IASTNode blockItem = getContainingBlockItem(fd);

		IASTNode declarationNode = blockItem;
		if (blockItem instanceof IASTDeclarationStatement
				&& ((IASTDeclarationStatement) blockItem).getDeclaration() instanceof IASTSimpleDeclaration) {
			declarationNode = ((IASTDeclarationStatement) blockItem).getDeclaration();
		} else {
			while (declarationNode != null && !(declarationNode instanceof IASTSimpleDeclaration))
				declarationNode = declarationNode.getParent();
		}

		if (declarationNode instanceof IASTSimpleDeclaration) {
			IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) declarationNode;
			IBinding struct = null;
			if (simpleDecl.getDeclSpecifier() instanceof IASTNamedTypeSpecifier) {
				struct = ((IASTNamedTypeSpecifier) simpleDecl.getDeclSpecifier()).getName().resolveBinding();
			} else if (simpleDecl.getDeclSpecifier() instanceof IASTElaboratedTypeSpecifier) {
				struct = ((IASTElaboratedTypeSpecifier) simpleDecl.getDeclSpecifier()).getName().resolveBinding();
			} else if (simpleDecl.getDeclSpecifier() instanceof IASTCompositeTypeSpecifier) {
				struct = ((IASTCompositeTypeSpecifier) simpleDecl.getDeclSpecifier()).getName().resolveBinding();
			}
			if (struct instanceof IType) {
				IType t = unwrapTypedefs((IType) struct);

				if (t instanceof ICompositeType) {
					return findFieldsByPrefix((ICompositeType) t, fd.getName().toCharArray());
				}
			}
		}

		return null;
	}

	public static IBinding[] findBindings(IScope scope, String name) {
		CASTName astName = new CASTName(name.toCharArray());

		// normal names
		astName.setPropertyInParent(STRING_LOOKUP_PROPERTY);
		Object o1 = lookup(scope, astName);

		IBinding[] b1 = null;
		if (o1 instanceof IBinding) {
			b1 = new IBinding[] { (IBinding) o1 };
		} else {
			b1 = (IBinding[]) o1;
		}

		// structure names
		astName.setPropertyInParent(STRING_LOOKUP_TAGS_PROPERTY);
		Object o2 = lookup(scope, astName);

		IBinding[] b2 = null;
		if (o2 instanceof IBinding) {
			b2 = new IBinding[] { (IBinding) o2 };
		} else {
			b2 = (IBinding[]) o2;
		}

		// label names
		List<ILabel> b3 = new ArrayList<>();
		do {
			char[] n = name.toCharArray();
			if (scope instanceof CFunctionScope) {
				IASTFunctionDefinition def = ASTQueries.findAncestorWithType(((CFunctionScope) scope).getPhysicalNode(),
						IASTFunctionDefinition.class);
				ILabel[] labels = getLabels(def);
				for (ILabel label : labels) {
					if (CharArrayUtils.equals(label.getNameCharArray(), n)) {
						b3.add(label);
						break;
					}
				}
				break;
			}
			try {
				scope = scope.getParent();
			} catch (DOMException e) {
				scope = null;
			}
		} while (scope != null);

		int c = (b1 == null ? 0 : b1.length) + (b2 == null ? 0 : b2.length) + b3.size();

		IBinding[] result = new IBinding[c];

		if (b1 != null)
			ArrayUtil.addAll(IBinding.class, result, b1);

		if (b2 != null)
			ArrayUtil.addAll(IBinding.class, result, b2);

		ArrayUtil.addAll(IBinding.class, result, b3.toArray(new IBinding[b3.size()]));

		return result;
	}

	static public boolean declaredBefore(IASTNode nodeA, IASTNode nodeB) {
		if (nodeB == null)
			return true;
		if (nodeB.getPropertyInParent() == STRING_LOOKUP_PROPERTY)
			return true;
		if (nodeB.getPropertyInParent() == STRING_LOOKUP_TAGS_PROPERTY)
			return true;

		if (nodeA instanceof ASTNode) {
			ASTNode nd = (ASTNode) nodeA;
			int pointOfDecl = 0;

			ASTNodeProperty prop = nd.getPropertyInParent();
			// point of declaration for a name is immediately after its complete declarator and before its initializer
			if (prop == IASTDeclarator.DECLARATOR_NAME || nd instanceof IASTDeclarator) {
				IASTDeclarator dtor = (IASTDeclarator) ((nd instanceof IASTDeclarator) ? nd : nd.getParent());
				while (dtor.getParent() instanceof IASTDeclarator)
					dtor = (IASTDeclarator) dtor.getParent();
				IASTInitializer init = dtor.getInitializer();
				if (init != null)
					pointOfDecl = ((ASTNode) init).getOffset() - 1;
				else
					pointOfDecl = ((ASTNode) dtor).getOffset() + ((ASTNode) dtor).getLength();
			}
			// point of declaration for an enumerator is immediately after it enumerator-definition
			else if (prop == IASTEnumerator.ENUMERATOR_NAME) {
				IASTEnumerator enumtor = (IASTEnumerator) nd.getParent();
				if (enumtor.getValue() != null) {
					ASTNode exp = (ASTNode) enumtor.getValue();
					pointOfDecl = exp.getOffset() + exp.getLength();
				} else {
					pointOfDecl = nd.getOffset() + nd.getLength();
				}
			} else {
				pointOfDecl = nd.getOffset() + nd.getLength();
			}

			return pointOfDecl < ((ASTNode) nodeB).getOffset();
		}

		return true;
	}

	/**
	 * Searches for the first function, struct or union enclosing the declaration the provided
	 * node belongs to and returns the binding for it. Returns <code>null</code>, if the declaration is not
	 * enclosed by any of the above constructs.
	 */
	public static IBinding findDeclarationOwner(IASTNode node, boolean allowFunction) {
		// search for declaration
		while (!(node instanceof IASTDeclaration)) {
			if (node == null)
				return null;

			node = node.getParent();
		}

		// search for enclosing binding
		IASTName name = null;
		node = node.getParent();
		for (; node != null; node = node.getParent()) {
			if (node instanceof IASTFunctionDefinition) {
				if (!allowFunction)
					continue;

				IASTDeclarator dtor = findInnermostDeclarator(((IASTFunctionDefinition) node).getDeclarator());
				if (dtor != null) {
					name = dtor.getName();
				}
				break;
			}
			if (node instanceof IASTCompositeTypeSpecifier) {
				name = ((IASTCompositeTypeSpecifier) node).getName();
				break;
			}
		}
		if (name == null)
			return null;

		return name.resolveBinding();
	}

	/**
	 * Determine whether an expression is a null pointer constant.
	 */
	public static boolean isNullPointerConstant(IASTExpression expression) {
		// [6.3.2.3] p3: An integer constant expression with the value 0, or such an expression cast
		// to void*, is called a null pointer constant.

		// Unwrap extra parentheses.
		if (expression instanceof IASTUnaryExpression) {
			IASTUnaryExpression unaryExpression = (IASTUnaryExpression) expression;
			if (unaryExpression.getOperator() == IASTUnaryExpression.op_bracketedPrimary) {
				return isNullPointerConstant(unaryExpression.getOperand());
			}
		}

		if (expression instanceof IASTCastExpression) {
			IASTCastExpression castExpression = (IASTCastExpression) expression;
			IType castType = createType(castExpression.getTypeId());
			if (castType.isSameType(CPointerType.VOID_POINTER)) {
				return isNullPointerConstant(castExpression.getOperand());
			}
		}

		IType expressionType = expression.getExpressionType();
		if (expressionType instanceof IBasicType) {
			IValue value = ValueFactory.create(expression);
			if (value != null && value.numberValue() != null) {
				return value.numberValue().longValue() == 0;
			}
		}

		return false;
	}

}
