/******************************************************************************* * Copyright (c) 2008, 2014 Wind River Systems, 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: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; import java.util.regex.Pattern; import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.dom.ast.IASTCompletionContext; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNameOwner; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IProblemBinding; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.internal.core.dom.Linkage; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.IASTInternalNameOwner; import org.eclipse.cdt.internal.core.dom.parser.IRecursionResolvingBinding; import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding; import org.eclipse.core.runtime.Assert; /** * Common base class for all sorts of c++ names: unqualified, qualified, operator and conversion * names plus template-ids */ public abstract class CPPASTNameBase extends ASTNode implements ICPPASTName { protected static final Pattern WHITESPACE_SEQ = Pattern.compile("\\s+"); //$NON-NLS-1$ /** * For test-purposes, only. */ public static boolean sAllowRecursionBindings = true; public static boolean sAllowNameComputation = true; private static final byte MAX_RESOLUTION_DEPTH= 6; protected final static class RecursionResolvingBinding extends ProblemBinding implements IRecursionResolvingBinding { public RecursionResolvingBinding(IASTName node, char[] arg) { super(node, IProblemBinding.SEMANTIC_RECURSION_IN_LOOKUP, arg); Assert.isTrue(sAllowRecursionBindings, getMessage()); } } private RecursionResolvingBinding createRecursionResolvingBinding() { // We create a recursion resolving binding when the resolution depth // exceeds MAX_RESOLUTION_DEPTH. If the resolution depth exceeds // MAX_RESOLUTION_DEPTH + 1, it means that attempting to create the // recursion resolving binding has led us back to trying to resolve // the binding for this name again, so the recursion isn't broken. // This can happen because the constructor of RecursionResolvingBinding // calls ProblemBinding.getMessage(), which can try to do name // resolution to build an argument string if one wasn't provided in the // ProblemBinding constructor. To break the recursion in a case // like, this we provide the argument string "(unknown)" instead. char[] args = (fResolutionDepth > MAX_RESOLUTION_DEPTH + 1) ? "(unknown)".toCharArray() : null; //$NON-NLS-1$ return new RecursionResolvingBinding(this, args); } private IBinding fBinding; private byte fResolutionDepth; private boolean fIsFinal; public final void incResolutionDepth() { if (fBinding == null && ++fResolutionDepth > MAX_RESOLUTION_DEPTH) { setBinding(createRecursionResolvingBinding()); } } /** * Called to perform the binding resolution. Subclasses may return lazy bindings that * will not be exposed within public API. */ protected abstract IBinding createIntermediateBinding(); /** * Resolves the name at least up to the intermediate binding and returns it. * @see ICPPTwoPhaseBinding */ @Override public IBinding resolvePreBinding() { if (fBinding == null) { if (++fResolutionDepth > MAX_RESOLUTION_DEPTH) { setBinding(createRecursionResolvingBinding()); } else { setBinding(createIntermediateBinding()); } } return fBinding; } @Override public IBinding resolveBinding() { if (fBinding == null) { if (++fResolutionDepth > MAX_RESOLUTION_DEPTH) { setBinding(createRecursionResolvingBinding()); } else { fIsFinal= false; final IBinding b= createIntermediateBinding(); if (b instanceof ProblemBinding) { ProblemBinding pb= (ProblemBinding) b; final IASTNode node= pb.getASTNode(); if (node == null || node.getParent() == null) { pb.setASTNode(this); } } setBinding(b); } } if (!fIsFinal) resolveFinalBinding(this); return fBinding; } /** * If this name has not yet been resolved at all, <code>null</code> will be returned. * Otherwise the intermediate or final binding for this name is returned. * @see ICPPTwoPhaseBinding */ @Override public IBinding getPreBinding() { return fBinding; } /** * If this name has not yet been resolved at all, <code>null</code> will be returned. * Otherwise the final binding for this name is returned. * @see ICPPTwoPhaseBinding */ @Override public IBinding getBinding() { final IBinding cand= fBinding; if (cand == null) return null; if (!fIsFinal) resolveFinalBinding(this); return fBinding; } private void resolveFinalBinding(CPPASTNameBase astName) { if (fBinding instanceof ICPPTwoPhaseBinding) { ICPPTwoPhaseBinding intermediateBinding= (ICPPTwoPhaseBinding) fBinding; if (++fResolutionDepth > MAX_RESOLUTION_DEPTH) { setBinding(createRecursionResolvingBinding()); } else { setBinding(intermediateBinding.resolveFinalBinding(astName)); } } fIsFinal= true; } @Override public void setBinding(IBinding binding) { fBinding= binding; fResolutionDepth= 0; } @Override public IASTName getLastName() { return this; } @Override public boolean isQualified() { IASTNode parent= getParent(); if (parent instanceof ICPPASTQualifiedName) { ICPPASTQualifiedName qn= (ICPPASTQualifiedName) parent; if (qn.isFullyQualified()) return true; ICPPASTNameSpecifier[] qualifier = qn.getQualifier(); if (qualifier.length > 0 && qualifier[0] == this) return false; return true; } return false; } @Override public final String toString() { return new String(toCharArray()); } @Override public IASTCompletionContext getCompletionContext() { IASTNode node = getParent(); while (node != null) { if (node instanceof IASTCompletionContext) { return (IASTCompletionContext) node; } node = node.getParent(); } return null; } @Override public int getRoleOfName(boolean allowResolution) { IASTNode parent = getParent(); if (parent instanceof IASTInternalNameOwner) { return ((IASTInternalNameOwner) parent).getRoleForName(this, allowResolution); } if (parent instanceof IASTNameOwner) { return ((IASTNameOwner) parent).getRoleForName(this); } return IASTNameOwner.r_unclear; } @Override public boolean isDeclaration() { IASTNode parent = getParent(); if (parent instanceof IASTNameOwner) { int role = ((IASTNameOwner) parent).getRoleForName(this); switch (role) { case IASTNameOwner.r_reference: case IASTNameOwner.r_unclear: return false; default: return true; } } return false; } @Override public boolean isReference() { IASTNode parent = getParent(); if (parent instanceof IASTNameOwner) { int role = ((IASTNameOwner) parent).getRoleForName(this); return role == IASTNameOwner.r_reference; } return false; } @Override public boolean isDefinition() { IASTNode parent = getParent(); if (parent instanceof IASTNameOwner) { int role = ((IASTNameOwner) parent).getRoleForName(this); return role == IASTNameOwner.r_definition; } return false; } @Override public ILinkage getLinkage() { return Linkage.CPP_LINKAGE; } }