blob: 53fe925b25f313ab6c5dfd7733e125e1e2bc685e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 IBM Corporation 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:
* Andrew Niefer (IBM Corporation) - 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)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.c;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
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.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
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.IASTSimpleDeclSpecifier;
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.IASTTypeIdExpression;
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.IScope;
import org.eclipse.cdt.core.dom.ast.c.ICASTCompositeTypeSpecifier;
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.ICScope;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFileSet;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
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.IASTAmbiguousDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousParameterDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousSimpleDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.IASTInternalScope;
import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
/**
* Base implementation for c-scopes
*/
public class CScope implements ICScope, IASTInternalScope {
/**
* ISO C:99 6.2.3 there are separate namespaces for various categories of
* identifiers: - label names (labels have ICFunctionScope) - tags of
* structures or unions : NAMESPACE_TYPE_TAG - members of structures or
* unions (members have ICCompositeTypeScope) - all other identifiers :
* NAMESPACE_TYPE_OTHER
*/
public static final int NAMESPACE_TYPE_TAG = 0;
public static final int NAMESPACE_TYPE_OTHER = 1;
public static final int NAMESPACE_TYPE_BOTH = 2;
private static final IndexFilter[] INDEX_FILTERS = {
new IndexFilter() { // namespace type tag
@Override
public boolean acceptBinding(IBinding binding) throws CoreException {
return IndexFilter.C_DECLARED_OR_IMPLICIT.acceptBinding(binding) &&
(binding instanceof ICompositeType || binding instanceof IEnumeration);
}
@Override
public boolean acceptLinkage(ILinkage linkage) {
return IndexFilter.C_DECLARED_OR_IMPLICIT.acceptLinkage(linkage);
}
},
new IndexFilter() { // namespace type other
@Override
public boolean acceptBinding(IBinding binding) throws CoreException {
return IndexFilter.C_DECLARED_OR_IMPLICIT.acceptBinding(binding) &&
!(binding instanceof ICompositeType || binding instanceof IEnumeration);
}
@Override
public boolean acceptLinkage(ILinkage linkage) {
return IndexFilter.C_DECLARED_OR_IMPLICIT.acceptLinkage(linkage);
}
},
// namespace type both
IndexFilter.C_DECLARED_OR_IMPLICIT
};
private IASTNode physicalNode = null;
private boolean isCached = false;
private final CharArrayObjectMap<?> mapsToNameOrBinding[] = { CharArrayObjectMap.EMPTY_MAP, CharArrayObjectMap.EMPTY_MAP };
private final EScopeKind kind;
public CScope(IASTNode physical, EScopeKind eKind) {
physicalNode = physical;
kind= eKind;
}
@Override
public EScopeKind getKind() {
return kind;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.IScope#getParent()
*/
@Override
public IScope getParent() {
return CVisitor.getContainingScope(physicalNode);
}
protected static class CollectNamesAction extends ASTVisitor {
private final char[] name;
private IASTName[] result = null;
CollectNamesAction(char[] n) {
name = n;
shouldVisitNames = true;
}
@Override
public int visit(IASTName n) {
ASTNodeProperty prop = n.getPropertyInParent();
if (prop == IASTElaboratedTypeSpecifier.TYPE_NAME ||
prop == IASTCompositeTypeSpecifier.TYPE_NAME ||
prop == IASTDeclarator.DECLARATOR_NAME) {
if (CharArrayUtils.equals(n.toCharArray(), name))
result = ArrayUtil.append(IASTName.class, result, n);
}
return PROCESS_CONTINUE;
}
@Override
public int visit(IASTStatement statement) {
if (statement instanceof IASTDeclarationStatement)
return PROCESS_CONTINUE;
return PROCESS_SKIP;
}
public IASTName[] getNames() {
return ArrayUtil.trim(IASTName.class, result);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.IScope#find(java.lang.String)
*/
@Override
public IBinding[] find(String name) {
return CVisitor.findBindings(this, name);
}
public IBinding getBinding(int namespaceType, char[] name) {
Object o= mapsToNameOrBinding[namespaceType].get(name);
if (o instanceof IBinding)
return (IBinding) o;
if (o instanceof IASTName)
return ((IASTName) o).resolveBinding();
if (o instanceof IASTName[]) {
return ((IASTName[]) o)[0].resolveBinding();
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.IScope#getPhysicalNode()
*/
@Override
public IASTNode getPhysicalNode() {
return physicalNode;
}
@Override
public void addName(IASTName name) {
final char[] nchars = name.toCharArray();
if (nchars.length == 0)
return;
int type = getNamespaceType(name);
@SuppressWarnings("unchecked")
CharArrayObjectMap<Object> map = (CharArrayObjectMap<Object>) mapsToNameOrBinding[type];
if (map == CharArrayObjectMap.EMPTY_MAP)
mapsToNameOrBinding[type] = map = new CharArrayObjectMap<Object>(1);
Object o= map.get(nchars);
if (o instanceof IASTName) {
if (o != name) {
map.put(nchars, new IASTName[] {(IASTName) o, name});
}
} else if (o instanceof IASTName[]) {
final IASTName[] names = (IASTName[]) o;
for (IASTName n : names) {
if (n == null)
break;
if (n == name)
return;
}
final IASTName[] newNames= ArrayUtil.append(IASTName.class, names, name);
if (newNames != names)
map.put(nchars, newNames);
} else {
map.put(nchars, name);
}
}
private int getNamespaceType(IASTName name) {
ASTNodeProperty prop = name.getPropertyInParent();
if (prop == IASTCompositeTypeSpecifier.TYPE_NAME ||
prop == IASTElaboratedTypeSpecifier.TYPE_NAME ||
prop == IASTEnumerationSpecifier.ENUMERATION_NAME ||
prop == CVisitor.STRING_LOOKUP_TAGS_PROPERTY) {
return NAMESPACE_TYPE_TAG;
}
return NAMESPACE_TYPE_OTHER;
}
@Override
public final IBinding getBinding(IASTName name, boolean resolve) {
return getBinding(name, resolve, IIndexFileSet.EMPTY);
}
@Override
public final IBinding[] getBindings(IASTName name, boolean resolve, boolean prefix) {
return getBindings(new ScopeLookupData(name, resolve, prefix));
}
@Override
public final IBinding getBinding(IASTName name, boolean resolve, IIndexFileSet fileSet) {
char[] c = name.toCharArray();
if (c.length == 0) {
return null;
}
populateCache();
final int type = getNamespaceType(name);
Object o = mapsToNameOrBinding[type].get(name.toCharArray());
if (o instanceof IBinding)
return (IBinding) o;
if (o instanceof IASTName) {
IBinding b= extractBinding((IASTName) o, resolve, name);
if (b != null)
return b;
} else if (o instanceof IASTName[]) {
for (IASTName n: ((IASTName[]) o)) {
if (n == null)
break;
IBinding b= extractBinding(n, resolve, name);
if (b != null)
return b;
}
}
IBinding result= null;
if (resolve && physicalNode instanceof IASTTranslationUnit) {
final IASTTranslationUnit tu = (IASTTranslationUnit)physicalNode;
IIndex index= tu.getIndex();
if (index != null) {
try {
IBinding[] bindings= index.findBindings(name.toCharArray(), INDEX_FILTERS[type], new NullProgressMonitor());
if (fileSet != null) {
bindings= fileSet.filterFileLocalBindings(bindings);
}
result= processIndexResults(name, bindings);
} catch(CoreException ce) {
CCorePlugin.log(ce);
}
}
}
return result;
}
private IBinding extractBinding(final IASTName candidate, boolean resolve, IASTName forName) {
if (!resolve || acceptDeclaredAfter(forName) || CVisitor.declaredBefore(candidate, forName)) {
if (resolve && candidate != forName) {
return candidate.resolveBinding();
}
return candidate.getBinding();
}
return null;
}
private boolean acceptDeclaredAfter(IASTName name) {
if (getKind() != EScopeKind.eGlobal)
return false;
final ASTNodeProperty propertyInParent = name.getPropertyInParent();
if (propertyInParent==IASTNamedTypeSpecifier.NAME ||
propertyInParent == IASTElaboratedTypeSpecifier.TYPE_NAME) {
return false;
}
IASTNode parent= name.getParent();
while (parent != null) {
if (parent instanceof IASTSimpleDeclSpecifier) {
if (((IASTSimpleDeclSpecifier) parent).getDeclTypeExpression() != null)
return false;
} else if (parent instanceof IASTTypeIdExpression) {
if (((IASTTypeIdExpression) parent).getOperator() == IASTTypeIdExpression.op_typeof)
return false;
}
parent= parent.getParent();
}
return true;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.c.ICScope#getBinding(org.eclipse.cdt.core.dom.ast.IASTName, boolean)
*/
/**
* @deprecated Use {@link #getBindings(ScopeLookupData)} instead
*/
@Deprecated
@Override
public final IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet fileSet) {
return getBindings(new ScopeLookupData(name, resolve, prefixLookup));
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.c.ICScope#getBinding(org.eclipse.cdt.core.dom.ast.IASTName, boolean)
*/
@Override
public final IBinding[] getBindings(ScopeLookupData lookup) {
char[] c = lookup.getLookupKey();
Object[] obj = null;
populateCache();
for (CharArrayObjectMap<?> map : mapsToNameOrBinding) {
if (lookup.isPrefixLookup()) {
IContentAssistMatcher matcher = ContentAssistMatcherFactory.getInstance().createMatcher(c);
Object[] keys = map.keyArray();
for (Object key2 : keys) {
char[] key = (char[]) key2;
if (matcher.match(key)) {
obj = ArrayUtil.append(obj, map.get(key));
}
}
} else {
obj = ArrayUtil.append(obj, map.get(c));
}
}
if (physicalNode instanceof IASTTranslationUnit) {
final IASTTranslationUnit tu = (IASTTranslationUnit) physicalNode;
IIndex index = tu.getIndex();
if (index != null) {
try {
IBinding[] bindings = lookup.isPrefixLookup() ?
index.findBindingsForContentAssist(lookup.getLookupKey(), true, INDEX_FILTERS[NAMESPACE_TYPE_BOTH], null) :
index.findBindings(lookup.getLookupKey(), INDEX_FILTERS[NAMESPACE_TYPE_BOTH], null);
IIndexFileSet filter = lookup.getIncludedFiles();
if (filter != null) {
bindings = filter.filterFileLocalBindings(bindings);
}
obj = ArrayUtil.addAll(Object.class, obj, bindings);
} catch (CoreException e) {
CCorePlugin.log(e);
}
}
}
obj = ArrayUtil.trim(Object.class, obj);
IBinding[] result = null;
for (Object element : obj) {
if (element instanceof IBinding) {
result = ArrayUtil.append(IBinding.class, result, (IBinding) element);
} else {
IASTName n= null;
if (element instanceof IASTName) {
n= (IASTName) element;
} else if (element instanceof IASTName[]) {
n = ((IASTName[]) element)[0];
}
if (n != null) {
IBinding b = n.getBinding();
if (b == null) {
if (lookup.isResolve() && n != lookup.getLookupPoint()) {
b = n.resolveBinding();
}
}
if (b != null) {
result = ArrayUtil.append(IBinding.class, result, b);
}
}
}
}
return ArrayUtil.trim(IBinding.class, result);
}
/**
* Index results from global scope, differ from ast results from translation unit scope. This routine
* is intended to fix results from the index to be consistent with ast scope behavior.
* @param name the name as it occurs in the ast
* @param bindings the set of candidate bindings
* @return the appropriate binding, or null if no binding is appropriate for the ast name
*/
private IBinding processIndexResults(IASTName name, IBinding[] bindings) {
if (bindings.length != 1)
return null;
return bindings[0];
}
@Override
public void populateCache() {
if (isCached)
return;
doPopulateCache();
isCached= true;
}
@Override
public void removeNestedFromCache(IASTNode container) {
if (mapsToNameOrBinding != null) {
removeFromMap(mapsToNameOrBinding[0], container);
removeFromMap(mapsToNameOrBinding[1], container);
}
}
private void removeFromMap(CharArrayObjectMap<?> map, IASTNode container) {
for (int i = 0; i < map.size(); i++) {
Object o= map.getAt(i);
if (o instanceof IASTName) {
if (container.contains((IASTNode) o)) {
final char[] key = map.keyAt(i);
map.remove(key, 0, key.length);
i--;
}
} else if (o instanceof IASTName[]) {
final IASTName[] set = (IASTName[]) o;
removeFromSet(set, container);
}
}
}
private void removeFromSet(IASTName[] set, IASTNode container) {
int j= 0;
for (int i = 0; i < set.length; i++) {
IASTName n= set[i];
if (n == null)
break;
if (container.contains(n)) {
set[i]= null;
} else if (i != j) {
set[j++]= n;
set[i]= null;
}
}
}
protected void doPopulateCache() {
final IASTNode scopeNode = physicalNode;
IASTNode[] nodes = null;
if (scopeNode instanceof IASTCompoundStatement) {
IASTCompoundStatement compound = (IASTCompoundStatement) scopeNode;
if (scopeNode.getParent() instanceof IASTFunctionDefinition) {
IASTFunctionDeclarator dtor = ((IASTFunctionDefinition) scopeNode.getParent()).getDeclarator();
if (dtor instanceof IASTStandardFunctionDeclarator) {
nodes = ((IASTStandardFunctionDeclarator) dtor).getParameters();
} else if (dtor instanceof ICASTKnRFunctionDeclarator) {
nodes = ((ICASTKnRFunctionDeclarator) dtor).getParameterDeclarations();
}
}
if (nodes == null || nodes.length == 0) {
nodes = compound.getStatements();
}
} else if (scopeNode instanceof IASTTranslationUnit) {
IASTTranslationUnit translation = (IASTTranslationUnit) scopeNode;
nodes = translation.getDeclarations();
} else if (scopeNode instanceof IASTStandardFunctionDeclarator) {
IASTStandardFunctionDeclarator dtor = (IASTStandardFunctionDeclarator) scopeNode;
nodes = dtor.getParameters();
} else if (scopeNode instanceof ICASTKnRFunctionDeclarator) {
ICASTKnRFunctionDeclarator dtor = (ICASTKnRFunctionDeclarator) scopeNode;
nodes = dtor.getParameterDeclarations();
} else if (scopeNode instanceof IASTForStatement) {
final IASTForStatement forStmt = (IASTForStatement) scopeNode;
nodes= new IASTNode[] {forStmt.getInitializerStatement()};
}
if (nodes != null) {
int idx = -1;
IASTNode node = nodes.length > 0 ? nodes[++idx] : null;
while (node != null) {
collectNames(node);
if (idx > -1 && ++idx < nodes.length) {
node = nodes[idx];
} else {
node = null;
if (nodes[0].getPropertyInParent() == ICASTKnRFunctionDeclarator.FUNCTION_PARAMETER
|| nodes[0].getPropertyInParent() == IASTStandardFunctionDeclarator.FUNCTION_PARAMETER) {
// function body, we were looking at parameters, now
// check the body itself
IASTCompoundStatement compound = null;
if (scopeNode instanceof IASTCompoundStatement) {
compound = (IASTCompoundStatement) scopeNode;
} else if (scopeNode instanceof IASTFunctionDeclarator) {
IASTNode n = scopeNode.getParent();
while (n instanceof IASTDeclarator)
n = n.getParent();
if (n instanceof IASTFunctionDefinition) {
compound = (IASTCompoundStatement) ((IASTFunctionDefinition) n).getBody();
}
}
if (compound != null) {
nodes = compound.getStatements();
if (nodes.length > 0) {
idx = 0;
node = nodes[0];
}
}
}
}
}
}
}
public void collectNames(IASTNode node) {
if (node instanceof IASTDeclaration) {
collectNames((IASTDeclaration) node);
} else if (node instanceof IASTParameterDeclaration) {
collectNames((IASTParameterDeclaration) node);
} else if (node instanceof IASTDeclarationStatement) {
collectNames(((IASTDeclarationStatement)node).getDeclaration());
}
}
private void collectNames(IASTParameterDeclaration paramDecl) {
if (paramDecl == null || paramDecl instanceof IASTAmbiguousParameterDeclaration)
return;
collectNames(paramDecl.getDeclarator());
collectNames(paramDecl.getDeclSpecifier(), false);
}
private void collectNames(IASTDeclarator dtor) {
IASTDeclarator innermost= null;
while (dtor != null) {
if (dtor instanceof IASTAmbiguousDeclarator) {
innermost= null;
break;
}
innermost= dtor;
dtor= dtor.getNestedDeclarator();
}
if (innermost != null)
ASTInternal.addName(this, innermost.getName());
}
private void collectNames(IASTDeclaration declaration) {
if (declaration instanceof IASTAmbiguousSimpleDeclaration)
return;
if (declaration instanceof IASTSimpleDeclaration) {
IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration;
IASTDeclarator[] declarators = simpleDeclaration.getDeclarators();
for (IASTDeclarator dtor : declarators) {
collectNames(dtor);
}
collectNames(simpleDeclaration.getDeclSpecifier(), declarators.length == 0);
} else if (declaration instanceof IASTFunctionDefinition) {
IASTFunctionDefinition functionDef = (IASTFunctionDefinition) declaration;
collectNames(functionDef.getDeclarator());
collectNames(functionDef.getDeclSpecifier(), false);
}
}
private void collectNames(IASTDeclSpecifier declSpec, boolean forceElabSpec) {
IASTName tempName = null;
if (declSpec instanceof ICASTElaboratedTypeSpecifier) {
if (forceElabSpec || physicalNode instanceof IASTTranslationUnit) {
tempName = ((ICASTElaboratedTypeSpecifier)declSpec).getName();
ASTInternal.addName(this, tempName);
}
} else if (declSpec instanceof ICASTCompositeTypeSpecifier) {
tempName = ((ICASTCompositeTypeSpecifier)declSpec).getName();
ASTInternal.addName(this, tempName);
//also have to check for any nested structs
IASTDeclaration[] nested = ((ICASTCompositeTypeSpecifier)declSpec).getMembers();
for (IASTDeclaration element : nested) {
if (element instanceof IASTSimpleDeclaration) {
IASTDeclSpecifier d = ((IASTSimpleDeclaration)element).getDeclSpecifier();
if (d instanceof ICASTCompositeTypeSpecifier || d instanceof IASTEnumerationSpecifier) {
collectNames(d, false);
}
}
}
} else if (declSpec instanceof ICASTEnumerationSpecifier) {
ICASTEnumerationSpecifier enumeration = (ICASTEnumerationSpecifier) declSpec;
tempName = enumeration.getName();
ASTInternal.addName(this, tempName);
//check enumerators
IASTEnumerator[] list = ((ICASTEnumerationSpecifier) declSpec).getEnumerators();
for (IASTEnumerator enumerator : list) {
if (enumerator == null) break;
tempName = enumerator.getName();
ASTInternal.addName(this, tempName);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ast.IScope#getScopeName()
*/
@Override
public IName getScopeName() {
if (physicalNode instanceof IASTCompositeTypeSpecifier) {
return ((IASTCompositeTypeSpecifier) physicalNode).getName();
}
return null;
}
@Override
public void addBinding(IBinding binding) {
int type = NAMESPACE_TYPE_OTHER;
if (binding instanceof ICompositeType || binding instanceof IEnumeration) {
type = NAMESPACE_TYPE_TAG;
}
@SuppressWarnings("unchecked")
CharArrayObjectMap<Object> map = (CharArrayObjectMap<Object>) mapsToNameOrBinding[type];
if (map == CharArrayObjectMap.EMPTY_MAP)
mapsToNameOrBinding[type] = map= new CharArrayObjectMap<Object>(2);
map.put(binding.getNameCharArray(), binding);
}
/**
* In case there was an ambiguity the cache has to be populated for a second time.
* However, we do not clear any names in order not to loose bindings.
*/
public void markAsUncached() {
isCached= false;
}
}