/******************************************************************************* * Copyright (c) 2009, 2016 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 * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.Stack; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTypeId; import org.eclipse.cdt.core.dom.ast.IScope; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization; import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; /** * Visitor to resolve AST ambiguities in the right order */ final class CPPASTAmbiguityResolver extends ASTVisitor { private int fSkipInitializers= 0; /* * The current nesting level of class definitions. * Used to handle processing of method bodies, which are deferred * until the end of the outermost class definition. */ private int fClassNestingLevel= 0; private HashSet<IASTDeclaration> fRepopulate= new HashSet<>(); /* * Nodes that have been deferred for later processing. * Currently used only for method bodies. */ private Deque<IASTNode> fDeferredNodes = new ArrayDeque<>(); /* * Used by visit(IASTDeclaration) to determine whether it should * process a function declaration now instead of deferring it * to later. There is a stack of them because, thanks to local * classes, function definitions can be nested inside each other. */ private Stack<IASTFunctionDefinition> fProcessNow = new Stack<>(); public CPPASTAmbiguityResolver() { super(false); includeInactiveNodes= true; shouldVisitAmbiguousNodes= true; shouldVisitDeclarations= true; shouldVisitDeclSpecifiers= true; shouldVisitInitializers= true; shouldVisitTranslationUnit= true; } @Override public int visit(ASTAmbiguousNode astAmbiguousNode) { IASTNode node= astAmbiguousNode.resolveAmbiguity(this); if (node instanceof IASTDeclarator) { while (node != null) { if (node instanceof IASTDeclaration) { fRepopulate.add((IASTDeclaration) node); break; } if (node instanceof IASTParameterDeclaration) { // If the parameter declaration belongs to a function declaration or // function definition we need to update the scope. IASTNode parent= node.getParent(); if (parent instanceof IASTDeclarator) { IASTDeclarator dtor= (IASTDeclarator) parent; if (dtor == ASTQueries.findTypeRelevantDeclarator(dtor) && ASTQueries.findOutermostDeclarator(dtor).getParent() instanceof IASTDeclaration) { repopulateScope((IASTParameterDeclaration) node); } } break; } if (node instanceof IASTExpression) { break; } node= node.getParent(); } } else if (node instanceof IASTDeclaration) { repopulateScope((IASTDeclaration) node); } return PROCESS_SKIP; } @Override public int visit(IASTDeclSpecifier declSpec) { if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { fClassNestingLevel++; } return PROCESS_CONTINUE; } @Override public int leave(IASTDeclSpecifier declSpec) { if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { fClassNestingLevel--; // Resolve class type definitions, such that the scope is available // during ambiguity resolution. ((ICPPASTCompositeTypeSpecifier) declSpec).getName().resolveBinding(); // Trigger computation of implicit members. if (declSpec instanceof CPPASTCompositeTypeSpecifier) ((CPPASTCompositeTypeSpecifier) declSpec).setAmbiguitiesResolved(); // If we are leaving the outermost class, process the bodies of // methods of the class and its nested classes. if (fClassNestingLevel == 0) { while (!fDeferredNodes.isEmpty()) { fDeferredNodes.removeFirst().accept(this); } } } return PROCESS_CONTINUE; } private boolean shouldProcessNow(IASTFunctionDefinition func) { return !fProcessNow.isEmpty() && fProcessNow.peek() == func; } @Override public int visit(IASTDeclaration decl) { if (decl instanceof IASTFunctionDefinition && !shouldProcessNow((IASTFunctionDefinition) decl)) { final IASTFunctionDefinition fdef= (IASTFunctionDefinition) decl; // Visit the declarator first, it may contain ambiguous template arguments needed // for associating the template declarations. ICPPASTFunctionDeclarator fdecl = (ICPPASTFunctionDeclarator) fdef.getDeclarator(); fSkipInitializers++; // Initializers may refer to class members declared later. fdecl.accept(this); fSkipInitializers--; fdef.getDeclSpecifier().accept(this); IASTTypeId trailingReturnType = fdecl.getTrailingReturnType(); if (trailingReturnType != null) { // Visit initializers inside the trailing return type that were skipped earlier. trailingReturnType.accept(this); } if (fClassNestingLevel > 0) { // If this is a method defined inline inside a class declaration, defer visiting // the remaining parts of the method (notably the body) until the end of the // class declaration has been reached. fDeferredNodes.add(decl); } else { // Otherwise, visit the remaining parts of the method now. To avoid duplicating // code in CPPASTFunctionDefinition.accept(), call accept() on the entire // definition, but push the function definition onto fProcessNow to avoid recursion. fProcessNow.push(fdef); decl.accept(this); fProcessNow.pop(); } return PROCESS_SKIP; } return PROCESS_CONTINUE; } @Override public int leave(IASTDeclaration declaration) { if (fRepopulate.remove(declaration)) { repopulateScope(declaration); } // We need to create class bindings for all definitions and for the specializations. // Otherwise, name resolution cannot access members or correct specialization. if (declaration instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration sdecl= (IASTSimpleDeclaration) declaration; IASTName name= null; IASTDeclSpecifier declspec = sdecl.getDeclSpecifier(); if (declspec instanceof IASTCompositeTypeSpecifier) { // Definition of a class[template[specialization]] name= ((IASTCompositeTypeSpecifier) declspec).getName().getLastName(); } else if (declspec instanceof ICPPASTElaboratedTypeSpecifier && sdecl.getDeclarators().length == 0) { ASTNodeProperty prop = declaration.getPropertyInParent(); if (prop == ICPPASTTemplateDeclaration.OWNED_DECLARATION || prop == ICPPASTTemplateSpecialization.OWNED_DECLARATION) { ICPPASTElaboratedTypeSpecifier elab= (ICPPASTElaboratedTypeSpecifier) declspec; if (!elab.isFriend()) { // Declaration of a class template specialization. name= elab.getName().getLastName(); } } } if (name instanceof ICPPASTTemplateId) { name.resolveBinding(); } } return PROCESS_CONTINUE; } @Override public int visit(IASTInitializer initializer) { if (fSkipInitializers > 0) return PROCESS_SKIP; return PROCESS_CONTINUE; } @Override public int leave(IASTTranslationUnit tu) { // As deferred method bodies are processed at the end of outermost // class definitions, there should be none left when the end of // the translation unit is reached. assert fDeferredNodes.isEmpty(); assert fProcessNow.isEmpty(); return PROCESS_CONTINUE; } private void repopulateScope(IASTDeclaration declaration) { IScope scope= CPPVisitor.getContainingNonTemplateScope(declaration); if (scope instanceof ICPPASTInternalScope) { CPPSemantics.populateCache((ICPPASTInternalScope) scope, declaration); } } private void repopulateScope(IASTParameterDeclaration declaration) { IScope scope= CPPVisitor.getContainingNonTemplateScope(declaration); if (scope instanceof ICPPASTInternalScope) { CPPSemantics.populateCache((ICPPASTInternalScope) scope, declaration); } } /** * If 'node' has been deferred for later processing, process it now. */ public void resolvePendingAmbiguities(IASTNode node) { for (IASTNode deferredNode : fDeferredNodes) { if (deferredNode == node) { // Temporarily set the class nesting level to 0, // to prevent the node just being deferred again. int classNestingLevel = fClassNestingLevel; fClassNestingLevel = 0; try { deferredNode.accept(this); } finally { fClassNestingLevel = classNestingLevel; } fDeferredNodes.remove(deferredNode); break; } } } }