diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java index ca38084592a..7e084fe3f48 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java @@ -8955,6 +8955,23 @@ public class AST2TemplateTests extends AST2TestBase { parseAndCheckBindings(); } + // template struct S {}; + // struct U {}; + // + // struct outer { + // struct inner { + // S foo() { + // return waldo<42>(0); + // } + // }; + // + // template + // static S waldo(int); + // }; + public void testAmbiguityResolutionInNestedClassMethodBody_485388() throws Exception { + parseAndCheckBindings(); + } + // template // struct Base { // template diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java index 8052ab07d03..3177d21a511 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java @@ -14,7 +14,6 @@ package org.eclipse.cdt.internal.core.dom.parser.cpp; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; -import java.util.Iterator; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTVisitor; @@ -48,9 +47,18 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; */ final class CPPASTAmbiguityResolver extends ASTVisitor { private int fSkipInitializers= 0; - private int fDeferFunctions= 1; + /* + * 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 fRepopulate= new HashSet<>(); - private Deque> fDeferredNodes= new ArrayDeque<>(); + /* + * Nodes that have been deferred for later processing. + * Currently used only for method bodies. + */ + private Deque fDeferredNodes = new ArrayDeque<>(); public CPPASTAmbiguityResolver() { super(false); @@ -98,8 +106,7 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { @Override public int visit(IASTDeclSpecifier declSpec) { if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { - fDeferFunctions++; - fDeferredNodes.add(new ArrayDeque()); + fClassNestingLevel++; } return PROCESS_CONTINUE; } @@ -107,7 +114,7 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { @Override public int leave(IASTDeclSpecifier declSpec) { if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { - fDeferFunctions--; + fClassNestingLevel--; // Resolve class type definitions, such that the scope is available // during ambiguity resolution. @@ -117,14 +124,20 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { if (declSpec instanceof CPPASTCompositeTypeSpecifier) ((CPPASTCompositeTypeSpecifier) declSpec).setAmbiguitiesResolved(); - processDeferredNodes(fDeferredNodes.removeLast()); + // 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; } @Override public int visit(IASTDeclaration decl) { - if (fDeferFunctions > 0 && decl instanceof IASTFunctionDefinition) { + if (fClassNestingLevel > 0 && decl instanceof IASTFunctionDefinition) { final IASTFunctionDefinition fdef= (IASTFunctionDefinition) decl; // Visit the declarator first, it may contain ambiguous template arguments needed @@ -140,7 +153,7 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { trailingReturnType.accept(this); } // Defer visiting the body of the function until the class body has been visited. - fDeferredNodes.getLast().add(decl); + fDeferredNodes.add(decl); return PROCESS_SKIP; } return PROCESS_CONTINUE; @@ -187,33 +200,15 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { return PROCESS_CONTINUE; } - @Override - public int visit(IASTTranslationUnit tu) { - fDeferredNodes.add(new ArrayDeque()); - return PROCESS_CONTINUE; - } - @Override public int leave(IASTTranslationUnit tu) { - fDeferFunctions= 0; - while (!fDeferredNodes.isEmpty()) { - processDeferredNodes(fDeferredNodes.removeLast()); - } + // 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(); return PROCESS_CONTINUE; } - private void processDeferredNodes(Deque deferredNodes) { - int deferFunctions = fDeferFunctions; - fDeferFunctions = 0; - try { - while (!deferredNodes.isEmpty()) { - deferredNodes.removeFirst().accept(this); - } - } finally { - fDeferFunctions = deferFunctions; - } - } - private void repopulateScope(IASTDeclaration declaration) { IScope scope= CPPVisitor.getContainingNonTemplateScope(declaration); if (scope instanceof ICPPASTInternalScope) { @@ -232,21 +227,19 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { * If 'node' has been deferred for later processing, process it now. */ public void resolvePendingAmbiguities(IASTNode node) { - Iterator> iterator = fDeferredNodes.descendingIterator(); - while (iterator.hasNext()) { - Deque deferred = iterator.next(); - for (IASTNode deferredNode : deferred) { - if (deferredNode == node) { - int deferFunctions = fDeferFunctions; - fDeferFunctions = 0; - try { - deferredNode.accept(this); - } finally { - fDeferFunctions = deferFunctions; - } - deferred.remove(deferredNode); - break; + 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; } } }