From 9a2e97825bdd952585a32b795f5fdca21614123a Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Mon, 11 Apr 2011 12:40:25 +0000 Subject: [PATCH] Bug 341747: Ambiguity between template-id and binary expression. --- .../parser/tests/ast2/AST2TemplateTests.java | 71 ++ .../cdt/core/parser/tests/ast2/AST2Tests.java | 8 +- .../parser/AbstractGNUSourceCodeParser.java | 74 +- .../core/dom/parser/c/GNUCSourceParser.java | 42 +- .../parser/cpp/CPPASTAmbiguityResolver.java | 18 +- .../parser/cpp/CPPASTTemplateIDAmbiguity.java | 181 +++++ .../dom/parser/cpp/GNUCPPSourceParser.java | 735 +++++++++++------- .../parser/cpp/NameOrTemplateIDVariants.java | 239 ++++++ .../dom/parser/cpp/TemplateIdStrategy.java | 81 ++ .../dom/parser/cpp/semantics/CPPVisitor.java | 11 +- .../dom/parser/cpp/semantics/LookupData.java | 30 +- .../cdt/ui/tests/text/AddIncludeTest.java | 2 +- 12 files changed, 1156 insertions(+), 336 deletions(-) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTemplateIDAmbiguity.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/NameOrTemplateIDVariants.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/TemplateIdStrategy.java 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 549fb0d78cf..6c28e18b783 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 @@ -24,6 +24,7 @@ import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement; +import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; import org.eclipse.cdt.core.dom.ast.IASTImplicitName; @@ -5314,4 +5315,74 @@ public class AST2TemplateTests extends AST2BaseTest { public void testPartialSpecAfterExplicitInst_339475() throws Exception { parseAndCheckBindings(); } + + + // template struct S { + // static int m(); + // }; + // template void g(int); + // int f(); + // int s; + // + // void test() { + // f < 0 > (1); // Function pointer + // g<0>(1); // Function call + // S<1 && 2>::m(); // m is member of S + // s<1 && 2>::f(); // f is global + // } + public void testTemplateIDAmbiguity_341747a() throws Exception { + IASTTranslationUnit tu= parseAndCheckBindings(); + IASTFunctionDefinition fdef= getDeclaration(tu, 4); + + IASTExpressionStatement stmt; + stmt= getStatement(fdef, 0); + assertTrue(stmt.getExpression() instanceof IASTBinaryExpression); + + stmt= getStatement(fdef, 1); + assertTrue(stmt.getExpression() instanceof IASTFunctionCallExpression); + + stmt= getStatement(fdef, 2); + assertTrue(stmt.getExpression() instanceof IASTFunctionCallExpression); + + stmt= getStatement(fdef, 0); + assertTrue(stmt.getExpression() instanceof IASTBinaryExpression); + } + + // const int a=0, b=1; + // template struct A{}; + // + // template struct S {}; + // struct X : S {}; + // + // template> struct T {}; + // struct Y : T> {}; + public void testTemplateIDAmbiguity_341747b() throws Exception { + parseAndCheckBindings(); + } + + // int a=0, b=1; + // bool bl= false; + // template struct S { + // int a; + // }; + // void test() { + // S< a::a; + // a < S::a; + // } + public void testTemplateIDAmbiguity_341747c() throws Exception { + parseAndCheckBindings(); + } + + // struct S { + // int B; + // }; + // template struct B {}; + // int c; + // void test() { + // S* a=0; + // a->B::c; + // } + public void testTemplateIDAmbiguity_341747d() throws Exception { + parseAndCheckBindings(); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2Tests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2Tests.java index 71586ca951d..f82112495b3 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2Tests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2Tests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 IBM Corporation and others. + * 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 @@ -7102,12 +7102,12 @@ public class AST2Tests extends AST2BaseTest { // void f() { // int a= public void testLargeExpression_294029() throws Exception { - // when running the test in a suite, it cannot handle more than 200 parenthesis. - // run as a single test it does > 600. + // when running the test in a suite, it cannot handle more than 160 parenthesis. + // run as a single test it does > 500. sValidateCopy= false; StringBuilder buf= new StringBuilder(); buf.append(getAboveComment()); - final int depth= 200; + final int depth= 160; for (int i = 0; i < depth; i++) { buf.append('('); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java index ef782241f1c..c9d35a3535c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java @@ -89,6 +89,9 @@ import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; * Base class for the c- and c++ parser. */ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { + public interface ITemplateIdStrategy { + } + protected static class FoundAggregateInitializer extends Exception { public final IASTDeclarator fDeclarator; public final IASTDeclSpecifier fDeclSpec; @@ -154,7 +157,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { * Information about the context in which a cast-expression is parsed: * in a binary expression, in a binary expression in a template-id, or elsewhere. */ - protected static enum CastExprCtx {eBExpr, eBExprInTmplID, eNotBExpr} + protected static enum CastExprCtx {eDirectlyInBExpr, eInBExpr, eNotInBExpr} protected static enum ExprKind {eExpression, eAssignment, eConstant} protected static final int DEFAULT_DESIGNATOR_LIST_SIZE = 4; @@ -921,7 +924,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { * Models a cast expression followed by an operator. Can be linked into a chain. * This is done right to left, such that a tree of variants can be built. */ - protected static class BinaryOperator { + public static class BinaryOperator { final int fOperatorToken; final int fLeftPrecedence; final int fRightPrecedence; @@ -949,9 +952,21 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { fExpression= expr; return e; } + + public IASTInitializerClause getExpression() { + return fExpression; + } + + public BinaryOperator getNext() { + return fNext; + } + + public void setNext(BinaryOperator next) { + fNext = next; + } } - protected final IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) throws BacktrackException { + public final IASTExpression buildExpression(BinaryOperator leftChain, IASTInitializerClause expr) { BinaryOperator rightChain= null; for (;;) { if (leftChain == null) { @@ -973,18 +988,13 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { } } - private IASTExpression buildExpression(IASTExpression left, BinaryOperator operator) throws BacktrackException { + private IASTExpression buildExpression(IASTExpression left, BinaryOperator operator) { int op, unaryOp= 0; final IASTInitializerClause right= operator.fExpression; switch(operator.fOperatorToken) { case IToken.tQUESTION: final IASTInitializerClause negative; if (operator.fNext == null || operator.fNext.fOperatorToken != IToken.tCOLON) { - if (LTcatchEOF(1) != IToken.tEOC || operator.fNext != null) { - assert false; - ASTNode node= (ASTNode) left; - throwBacktrack(node.getOffset(), node.getLength()); - } negative= null; } else { negative= operator.fNext.fExpression; @@ -1116,25 +1126,26 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { default: assert false; - ASTNode node= (ASTNode) left; - throwBacktrack(node.getOffset(), node.getLength()); return null; } IASTExpression result= buildBinaryExpression(op, left, right, calculateEndOffset(right)); final CastAmbiguityMarker am = operator.fAmbiguityMarker; if (am != null) { - assert unaryOp != 0; - result = createCastVsBinaryExpressionAmbiguity((IASTBinaryExpression) result, - am.getTypeIdForCast(), unaryOp, am.getUnaryOperatorOffset()); + if (unaryOp != 0) { + result = createCastVsBinaryExpressionAmbiguity((IASTBinaryExpression) result, + am.getTypeIdForCast(), unaryOp, am.getUnaryOperatorOffset()); + } else { + assert false; + } } return result; } protected abstract IASTExpression expression() throws BacktrackException, EndOfFileException; protected abstract IASTExpression constantExpression() throws BacktrackException, EndOfFileException; - protected abstract IASTExpression unaryExpression(CastExprCtx ctx) throws BacktrackException, EndOfFileException; - protected abstract IASTExpression primaryExpression(CastExprCtx ctx) throws BacktrackException, EndOfFileException; + protected abstract IASTExpression unaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws BacktrackException, EndOfFileException; + protected abstract IASTExpression primaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws BacktrackException, EndOfFileException; protected abstract IASTTypeId typeId(DeclarationOptions option) throws EndOfFileException, BacktrackException; private final static class CastAmbiguityMarker extends ASTNode implements IASTExpression { @@ -1186,7 +1197,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { } } - protected final IASTExpression castExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected final IASTExpression castExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { if (LT(1) == IToken.tLPAREN) { final IToken mark= mark(); final int startingOffset= mark.getOffset(); @@ -1199,7 +1210,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { if (typeId != null && LT(1) == IToken.tRPAREN) { consume(); boolean unaryFailed= false; - if (ctx != CastExprCtx.eNotBExpr) { + if (ctx == CastExprCtx.eDirectlyInBExpr) { switch (LT(1)){ // ambiguity with unary operator case IToken.tPLUS: case IToken.tMINUS: @@ -1208,7 +1219,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { IToken markEnd= mark(); backup(mark); try { - IASTExpression unary= unaryExpression(CastExprCtx.eNotBExpr); + IASTExpression unary= unaryExpression(CastExprCtx.eInBExpr, strat); return new CastAmbiguityMarker(unary, typeId, operatorOffset); } catch (BacktrackException bt) { backup(markEnd); @@ -1218,7 +1229,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { } try { boolean couldBeFunctionCall= LT(1) == IToken.tLPAREN; - IASTExpression rhs= castExpression(ctx); + IASTExpression rhs= castExpression(ctx, strat); CastAmbiguityMarker ca= null; if (rhs instanceof CastAmbiguityMarker) { @@ -1226,12 +1237,13 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { rhs= ca.getExpression(); assert !(rhs instanceof CastAmbiguityMarker); } - IASTCastExpression result= buildCastExpression(IASTCastExpression.op_cast, typeId, rhs, startingOffset, calculateEndOffset(rhs)); + IASTCastExpression result = buildCastExpression(IASTCastExpression.op_cast, + typeId, rhs, startingOffset, calculateEndOffset(rhs)); if (!unaryFailed && couldBeFunctionCall && rhs instanceof IASTCastExpression == false) { IToken markEnd= mark(); backup(mark); try { - IASTExpression expr= primaryExpression(ctx); + IASTExpression expr= primaryExpression(ctx, strat); IASTFunctionCallExpression fcall = nodeFactory.newFunctionCallExpression(expr, (IASTExpression[]) null); IASTAmbiguousExpression ambiguity = createAmbiguousCastVsFunctionCallExpression(result, fcall); ((ASTNode) ambiguity).setOffsetAndLength((ASTNode) result); @@ -1249,7 +1261,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { } backup(mark); } - return unaryExpression(ctx); + return unaryExpression(ctx, strat); } protected abstract IASTTranslationUnit getTranslationUnit(); @@ -1363,7 +1375,8 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { abstract protected IASTExpression buildBinaryExpression(int operator, IASTExpression expr1, IASTInitializerClause expr2, int lastOffset); - private IASTExpression createCastVsBinaryExpressionAmbiguity(IASTBinaryExpression expr, final IASTTypeId typeid, int unaryOperator, int unaryOpOffset) { + private IASTExpression createCastVsBinaryExpressionAmbiguity(IASTBinaryExpression expr, + final IASTTypeId typeid, int unaryOperator, int unaryOpOffset) { IASTUnaryExpression unary= nodeFactory.newUnaryExpression(unaryOperator, null); ((ASTNode) unary).setOffset(unaryOpOffset); IASTCastExpression castExpr = buildCastExpression(IASTCastExpression.op_cast, typeid, unary, 0, 0); @@ -1372,9 +1385,9 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { return result; } - protected IASTExpression unaryExpression(int operator, CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected IASTExpression unaryExpression(int operator, CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { final IToken operatorToken= consume(); - IASTExpression operand= castExpression(ctx); + IASTExpression operand= castExpression(ctx, strat); CastAmbiguityMarker ca= null; if (operand instanceof CastAmbiguityMarker) { @@ -1814,13 +1827,12 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { return result; } - - protected IASTCastExpression buildCastExpression(int op, IASTTypeId typeId, IASTExpression operand, int offset, int endOffset) { + protected IASTCastExpression buildCastExpression(int op, IASTTypeId typeId, + IASTExpression operand, int offset, int endOffset) { IASTCastExpression result = nodeFactory.newCastExpression(op, typeId, operand); ((ASTNode) result).setOffsetAndLength(offset, endOffset - offset); return result; } - /** * There are many ambiguities in C and C++ between expressions and declarations. @@ -2173,7 +2185,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { } protected IASTExpression parseTypeidInParenthesisOrUnaryExpression(boolean exprIsLimitedToParenthesis, - int offset, int typeExprKind, int unaryExprKind, CastExprCtx ctx) throws BacktrackException, EndOfFileException { + int offset, int typeExprKind, int unaryExprKind, CastExprCtx ctx, ITemplateIdStrategy strat) throws BacktrackException, EndOfFileException { IASTTypeId typeid; IASTExpression expr= null; IToken typeidLA= null; @@ -2234,7 +2246,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { expr= expression(); endOffset2= consumeOrEOC(IToken.tRPAREN).getEndOffset(); } else { - expr= unaryExpression(ctx); + expr= unaryExpression(ctx, strat); if (expr instanceof CastAmbiguityMarker) { ca= (CastAmbiguityMarker) expr; expr= ca.getExpression(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java index 39f3f5ce354..19c901da95a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2010 IBM Corporation and others. + * Copyright (c) 2005, 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 @@ -467,7 +467,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { int lt1; int conditionCount= 0; BinaryOperator lastOperator= null; - IASTExpression lastExpression= castExpression(CastExprCtx.eBExpr); + IASTExpression lastExpression= castExpression(CastExprCtx.eDirectlyInBExpr, null); loop: while (true) { lt1= LT(1); switch(lt1) { @@ -564,7 +564,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { } consume(); // consume operator - lastExpression= castExpression(CastExprCtx.eBExpr); // next cast expression + lastExpression= castExpression(CastExprCtx.eDirectlyInBExpr, null); // next cast expression } // Check for incomplete conditional expression @@ -581,38 +581,38 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { ((ASTNode) result).setOffsetAndLength(o, lastOffset - o); return result; } - + @Override - protected IASTExpression unaryExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected IASTExpression unaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { switch (LT(1)) { case IToken.tSTAR: - return unaryExpression(IASTUnaryExpression.op_star, ctx); + return unaryExpression(IASTUnaryExpression.op_star, ctx, strat); case IToken.tAMPER: - return unaryExpression(IASTUnaryExpression.op_amper, ctx); + return unaryExpression(IASTUnaryExpression.op_amper, ctx, strat); case IToken.tPLUS: - return unaryExpression(IASTUnaryExpression.op_plus, ctx); + return unaryExpression(IASTUnaryExpression.op_plus, ctx, strat); case IToken.tMINUS: - return unaryExpression(IASTUnaryExpression.op_minus, ctx); + return unaryExpression(IASTUnaryExpression.op_minus, ctx, strat); case IToken.tNOT: - return unaryExpression(IASTUnaryExpression.op_not, ctx); + return unaryExpression(IASTUnaryExpression.op_not, ctx, strat); case IToken.tBITCOMPLEMENT: - return unaryExpression(IASTUnaryExpression.op_tilde, ctx); + return unaryExpression(IASTUnaryExpression.op_tilde, ctx, strat); case IToken.tINCR: - return unaryExpression(IASTUnaryExpression.op_prefixIncr, ctx); + return unaryExpression(IASTUnaryExpression.op_prefixIncr, ctx, strat); case IToken.tDECR: - return unaryExpression(IASTUnaryExpression.op_prefixDecr, ctx); + return unaryExpression(IASTUnaryExpression.op_prefixDecr, ctx, strat); case IToken.t_sizeof: return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), - IASTTypeIdExpression.op_sizeof, IASTUnaryExpression.op_sizeof, ctx); + IASTTypeIdExpression.op_sizeof, IASTUnaryExpression.op_sizeof, ctx, strat); case IGCCToken.t___alignof__: return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), - IASTTypeIdExpression.op_alignof, IASTUnaryExpression.op_alignOf, ctx); + IASTTypeIdExpression.op_alignof, IASTUnaryExpression.op_alignOf, ctx, strat); default: - return postfixExpression(ctx); + return postfixExpression(ctx, strat); } } - protected IASTExpression postfixExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + private IASTExpression postfixExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { IASTExpression firstExpression = null; switch (LT(1)) { case IToken.tLPAREN: @@ -632,11 +632,11 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { } catch (BacktrackException bt) { } backup(m); - firstExpression= primaryExpression(ctx); + firstExpression= primaryExpression(ctx, strat); break; default: - firstExpression = primaryExpression(ctx); + firstExpression = primaryExpression(ctx, strat); break; } @@ -748,7 +748,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { } @Override - protected IASTExpression primaryExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected IASTExpression primaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { IToken t = null; IASTLiteralExpression literalExpression = null; switch (LT(1)) { @@ -1114,7 +1114,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { simpleType= IASTSimpleDeclSpecifier.t_typeof; consume(IGCCToken.t_typeof); typeofExpression = parseTypeidInParenthesisOrUnaryExpression(false, LA(1).getOffset(), - IASTTypeIdExpression.op_typeof, -1, CastExprCtx.eNotBExpr); + IASTTypeIdExpression.op_typeof, -1, CastExprCtx.eNotInBExpr, null); encounteredTypename= true; endOffset= calculateEndOffset(typeofExpression); 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 d3fd1f5a667..4ab0acfe80b 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. + * Copyright (c) 2009, 2011 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 @@ -14,6 +14,7 @@ import java.util.HashSet; import java.util.LinkedList; 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.IASTDeclarationStatement; @@ -21,11 +22,14 @@ 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.IScope; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; 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; @@ -115,6 +119,18 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { if (fRepopulate.remove(declaration)) { repopulateScope(declaration); } + // Explicit and partial class template specializations need to be resolved right away, + // otherwise we fail to correctly resolve qualified names that depend on a partial specialization. + if (declaration instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration sdecl= (IASTSimpleDeclaration) declaration; + IASTDeclSpecifier declspec = sdecl.getDeclSpecifier(); + if (declspec instanceof IASTCompositeTypeSpecifier && sdecl.getDeclarators().length == 0) { + IASTName name= ((IASTCompositeTypeSpecifier) declspec).getName().getLastName(); + if (name instanceof ICPPASTTemplateId) { + name.resolveBinding(); + } + } + } return PROCESS_CONTINUE; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTemplateIDAmbiguity.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTemplateIDAmbiguity.java new file mode 100644 index 00000000000..4cb5929ce15 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTTemplateIDAmbiguity.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2011 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.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition; +import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode; +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser; +import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.BinaryOperator; +import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; +import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousExpression; +import org.eclipse.cdt.internal.core.dom.parser.cpp.NameOrTemplateIDVariants.BranchPoint; +import org.eclipse.cdt.internal.core.dom.parser.cpp.NameOrTemplateIDVariants.Variant; + +/** + * Models expression variants for the ambiguity of a template id. + */ +public class CPPASTTemplateIDAmbiguity extends ASTAmbiguousNode implements IASTAmbiguousExpression { + + private BinaryOperator fLastOperator; + private IASTInitializerClause fLastExpression; + private final BranchPoint fVariants; + private IASTNode[] fNodes; + private AbstractGNUSourceCodeParser fParser; + + public CPPASTTemplateIDAmbiguity(AbstractGNUSourceCodeParser parser, BinaryOperator lastOperator, IASTInitializerClause expr, + BranchPoint variants) { + fParser= parser; + fLastOperator= lastOperator; + fLastExpression= expr; + fVariants= variants; + } + + @Override + public IASTNode resolveAmbiguity(ASTVisitor resolver) { + final IASTAmbiguityParent owner= (IASTAmbiguityParent) getParent(); + IASTNode nodeToReplace= this; + + // Try all variants and under the ones with correct template-ids select the one with + // the most template-ids. + int minOffset= -1; + for (BranchPoint v= fVariants; v != null; v= v.getNext()) { + Variant selected= null; + int bestCount= 0; + for (Variant q= v.getFirstVariant(); q != null ; q=q.getNext()) { + final IASTName[] templateNames = q.getTemplateNames(); + if (templateNames.length > bestCount) { + // Don't check branch-points inside of a selected variant. + final IASTExpression expression = q.getExpression(); + if (((ASTNode) expression).getOffset() < minOffset) + break; + + // Setup the ast to use the alternative + owner.replace(nodeToReplace, expression); + nodeToReplace= expression; + + // Handle nested ambiguities first + expression.accept(resolver); + + int count= checkNames(templateNames); + if (count > bestCount) { + selected= q; + bestCount= count; + } + } + } + + // Adjust the operator sequence + if (selected != null) { + minOffset= selected.getRightOffset(); + BinaryOperator targetOp = selected.getTargetOperator(); + if (targetOp == null) { + fLastExpression= selected.getExpression(); + fLastOperator= v.getLeftOperator(); + } else { + targetOp.exchange(selected.getExpression()); + targetOp.setNext(v.getLeftOperator()); + } + } + } + + // Important: Before building the expression remove it from the owner + owner.replace(nodeToReplace, this); + + // Create the expression and replace it + IASTExpression expr = fParser.buildExpression(fLastOperator, fLastExpression); + owner.replace(this, expr); + + // Resolve further ambiguities within the new expression. + expr.accept(resolver); + return expr; + } + + private int checkNames(final IASTName[] templateNames) { + int count= 0; + for (IASTName templateName : templateNames) { + if (templateName.getTranslationUnit() != null) { + IBinding b= templateName.resolveBinding(); + if (b instanceof IProblemBinding) { + if (!containsFunctionTemplate(((IProblemBinding) b).getCandidateBindings())) + return -1; + count++; + } else if (b instanceof ICPPSpecialization || b instanceof ICPPTemplateDefinition + || b instanceof ICPPConstructor + || (b instanceof IFunction && b instanceof ICPPUnknownBinding)) { + count++; + } else { + return -1; + } + } + } + return count; + } + + private boolean containsFunctionTemplate(IBinding[] candidateBindings) { + for (IBinding cand : candidateBindings) { + if (cand instanceof ICPPFunctionTemplate || + (cand instanceof ICPPFunction && cand instanceof ICPPSpecialization)) { + return true; + } + } + return false; + } + + @Override + public IASTNode[] getNodes() { + if (fNodes == null) { + List nl= new ArrayList(); + nl.add(fLastExpression); + BinaryOperator op= fLastOperator; + while (op != null) { + nl.add(op.getExpression()); + op= op.getNext(); + } + Collections.reverse(nl); + fNodes= nl.toArray(new IASTNode[nl.size()]); + } + return fNodes; + } + + public IASTExpression copy() { + throw new UnsupportedOperationException(); + } + + public IASTExpression copy(CopyStyle style) { + throw new UnsupportedOperationException(); + } + + public void addExpression(IASTExpression e) { + throw new UnsupportedOperationException(); + } + + public IASTExpression[] getExpressions() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java index 917e8cbae11..9a80cbbe35a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2002, 2010 IBM Corporation and others. + * Copyright (c) 2002, 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 @@ -139,6 +139,8 @@ import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousDeclarator; import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousExpression; import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousStatement; +import org.eclipse.cdt.internal.core.dom.parser.cpp.NameOrTemplateIDVariants.BranchPoint; +import org.eclipse.cdt.internal.core.dom.parser.cpp.NameOrTemplateIDVariants.Variant; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; /** @@ -162,7 +164,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { private char[] currentClassName; private final ICPPNodeFactory nodeFactory; - private boolean fInTemplateParameterList; + private TemplateIdStrategy fTemplateParameterListStrategy; public GNUCPPSourceParser(IScanner scanner, ParserMode mode, IParserLogService log, ICPPParserExtensionConfiguration config) { @@ -204,14 +206,27 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { throw backtrack; } + private IASTName qualifiedName() throws BacktrackException, EndOfFileException { + TemplateIdStrategy strat= new TemplateIdStrategy(); + IToken m= mark(); + for(;;) { + try { + return qualifiedName(strat, CastExprCtx.eNotInBExpr); + } catch (BacktrackException e) { + if (strat.setNextAlternative()) { + backup(m); + } else { + throw e; + } + } + } + } + /** - * Parse a name. - * name ::= ("::")? name2 ("::" name2)* - * name2 ::= IDENTIFER | template-id - * - * @throws BacktrackException request a backtrack + * Parses a qualified name. */ - private IASTName qualifiedName(CastExprCtx ctx) throws BacktrackException, EndOfFileException { + private IASTName qualifiedName(ITemplateIdStrategy s, CastExprCtx ctx) throws BacktrackException, EndOfFileException { + final TemplateIdStrategy strat= (TemplateIdStrategy) s; ICPPASTQualifiedName qname= null; IASTName name= null; final int offset= LA(1).getOffset(); @@ -225,10 +240,10 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { boolean mustBeLast= false; boolean haveName= false; loop: for(;;) { - boolean forceTemplate= false; + boolean keywordTemplate= false; if (qname != null && LT(1) == IToken.t_template) { consume(); - forceTemplate= true; + keywordTemplate= true; } int destructorOffset= -1; @@ -250,7 +265,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { break; default: - if (!haveName || destructorOffset >= 0 || forceTemplate) { + if (!haveName || destructorOffset >= 0 || keywordTemplate) { throwBacktrack(LA(1)); } name= nodeFactory.newName(CharArrayUtils.EMPTY); @@ -261,9 +276,33 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } haveName= true; - name= addTemplateArguments(name, ctx); - endOffset= calculateEndOffset(name); - if (qname != null) { + + // Check for template-id + if (LTcatchEOF(1) == IToken.tLT) { + final boolean inBinaryExpression = ctx != CastExprCtx.eNotInBExpr; + final int haveArgs = haveTemplateArguments(inBinaryExpression); + boolean templateID= true; + if (!keywordTemplate) { + if (haveArgs == -1) { + templateID= false; + } else if (haveArgs == 0) { + if (strat.ignoreTemplateID()) { + templateID= false; + } else { + strat.addTemplateName(name); + } + } + } + if (templateID) { + if (haveArgs == -1) + throwBacktrack(LA(1)); + + name= addTemplateArguments(name, strat); + } + } + + endOffset= calculateEndOffset(name); + if (qname != null) { qname.addName(name); } @@ -279,11 +318,11 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { qname.addName(name); } } - if (qname == null) - return name; - - setRange(qname, offset, endOffset); - return qname; + if (qname != null) { + setRange(qname, offset, endOffset); + name= qname; + } + return name; } private IASTName buildName(int destructorOffset, IToken nt) { @@ -309,36 +348,22 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return name; } - private IASTName addTemplateArguments(IASTName templateName, CastExprCtx ctx) throws EndOfFileException, BacktrackException { - boolean isAmbiguous= false; - switch (canBeTemplateArguments(ctx)) { - case -1: - return templateName; - case 0: - isAmbiguous= true; - break; - } - - IToken secondMark = mark(); + private IASTName addTemplateArguments(IASTName templateName, TemplateIdStrategy strat) throws EndOfFileException, BacktrackException { + // Parse for template arguments consume(IToken.tLT); - try { - List list = templateArgumentList(isAmbiguous); - IToken end= LA(1); - switch (end.getType()) { - case IToken.tGT_in_SHIFTR: - case IToken.tGT: - consume(); - break; - case IToken.tEOC: - break; - default: - throw backtrack; - } - return buildTemplateID(templateName, end.getEndOffset(), list); - } catch (BacktrackException bt) { - backup(secondMark); - return templateName; - } + List list = templateArgumentList(strat); + IToken end= LA(1); + switch (end.getType()) { + case IToken.tGT_in_SHIFTR: + case IToken.tGT: + consume(); + break; + case IToken.tEOC: + break; + default: + throw backtrack; + } + return buildTemplateID(templateName, end.getEndOffset(), list); } private ICPPASTTemplateId buildTemplateID(IASTName templateName, int endOffset, List args) { @@ -360,23 +385,23 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { * Makes a fast check whether there could be template arguments. * -1: no, 0: ambiguous, 1: yes */ - private int canBeTemplateArguments(CastExprCtx ctx) throws EndOfFileException, BacktrackException { - if (LTcatchEOF(1) != IToken.tLT) - return -1; - + private static final int NO_TEMPLATE_ID= -1; + private static final int AMBIGUOUS_TEMPLATE_ID= 0; + private static final int TEMPLATE_ID= 1; + private int haveTemplateArguments(boolean inBinaryExpression) throws EndOfFileException, BacktrackException { final IToken mark= mark(); try { consume(); int nk= 0; int depth= 0; - int angleDepth= 0; + int angleDepth= 1; int limit= 10000; while(--limit > 0) { switch (consume().getType()) { case IToken.tEOC: case IToken.tCOMPLETION: - return ctx == CastExprCtx.eNotBExpr ? 0 : -1; + return AMBIGUOUS_TEMPLATE_ID; case IToken.tLT: if (nk == 0) { @@ -386,86 +411,17 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.tGT_in_SHIFTR: case IToken.tGT: if (nk == 0) { - if (--angleDepth < 0) { - if (ctx == CastExprCtx.eNotBExpr) - return 1; - - int lt1= LTcatchEOF(1); - if (lt1 == IToken.tGT_in_SHIFTR) { - if (ctx != CastExprCtx.eBExprInTmplID) - return -1; - // do the same check for the enclosing template - lt1= LTcatchEOF(2); - } - switch (lt1) { - // Can be some cast-expression or continuation after template-id - case IToken.tCOLONCOLON: // CT::member - case IToken.tLPAREN: // ft(args) - return 0; - // Start of other expressions (then we can't have a template) - // unary expression - case IToken.tMINUS: - case IToken.tPLUS: - case IToken.tAMPER: - case IToken.tSTAR: - case IToken.tNOT: - case IToken.tBITCOMPLEMENT: - case IToken.tINCR: - case IToken.tDECR: - case IToken.t_new: - case IToken.t_delete: - case IToken.t_sizeof: - case IGCCToken.t___alignof__: - // postfix expression - case IToken.t_typename: - case IToken.t_char: - case IToken.t_char16_t: - case IToken.t_char32_t: - case IToken.t_wchar_t: - case IToken.t_bool: - case IToken.t_short: - case IToken.t_int: - case IToken.t_long: - case IToken.t_signed: - case IToken.t_unsigned: - case IToken.t_float: - case IToken.t_double: - case IToken.t_dynamic_cast: - case IToken.t_static_cast: - case IToken.t_reinterpret_cast: - case IToken.t_const_cast: - case IToken.t_typeid: - // primary expression - case IToken.tINTEGER: - case IToken.tFLOATINGPT: - case IToken.tSTRING: - case IToken.tLSTRING: - case IToken.tUTF16STRING: - case IToken.tUTF32STRING: - case IToken.tCHAR: - case IToken.tLCHAR: - case IToken.tUTF16CHAR: - case IToken.tUTF32CHAR: - case IToken.t_false: - case IToken.t_true: - case IToken.t_this: - case IToken.tIDENTIFIER: - case IToken.t_operator: - case IToken.tCOMPLETION: - return -1; - // ending an expression - case IToken.tSEMI: - case IToken.tCOMMA: - case IToken.tRBRACE: - case IToken.tRBRACKET: - case IToken.tRPAREN: - case IToken.tELLIPSIS: // pack-expansion - return 1; - // don't know - default: - return 0; - } - } + --angleDepth; + if (!inBinaryExpression) + return angleDepth == 0 ? TEMPLATE_ID : AMBIGUOUS_TEMPLATE_ID; + + int end= endsTemplateIDInBinaryExpression(); + if (end == NO_TEMPLATE_ID) { + if (angleDepth == 0) + return NO_TEMPLATE_ID; + } else { + return AMBIGUOUS_TEMPLATE_ID; + } } break; case IToken.tLBRACKET: @@ -503,7 +459,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.tLBRACE: case IToken.tRBRACE: if (nk == 0) { - return -1; + return NO_TEMPLATE_ID; } break; } @@ -514,11 +470,104 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } } - private List templateArgumentList(boolean isAmbiguous) throws EndOfFileException, BacktrackException { + /** + * If '>' is followed by an expression, then it denotes the binary operator, + * else it is the end of a template-id, or special-cast. + */ + private int endsTemplateIDInBinaryExpression() { + int lt1= LTcatchEOF(1); + switch (lt1) { + // Can be start of expression, or the scope operator applied to the template-id + case IToken.tCOLONCOLON: // 'CT::member' or 'c<1 && 2 > ::g' + return AMBIGUOUS_TEMPLATE_ID; + + // Can be start of expression or the function-call operator applied to a template-id + case IToken.tLPAREN: // 'ft(args)' or 'c<1 && 2 > (x+y)' + return AMBIGUOUS_TEMPLATE_ID; + + // Start of unary expression + case IToken.tMINUS: + case IToken.tPLUS: + case IToken.tAMPER: + case IToken.tSTAR: + case IToken.tNOT: + case IToken.tBITCOMPLEMENT: + case IToken.tINCR: + case IToken.tDECR: + case IToken.t_new: + case IToken.t_delete: + case IToken.t_sizeof: + case IGCCToken.t___alignof__: + return NO_TEMPLATE_ID; + + // Start of a postfix expression + case IToken.t_typename: + case IToken.t_char: + case IToken.t_char16_t: + case IToken.t_char32_t: + case IToken.t_wchar_t: + case IToken.t_bool: + case IToken.t_short: + case IToken.t_int: + case IToken.t_long: + case IToken.t_signed: + case IToken.t_unsigned: + case IToken.t_float: + case IToken.t_double: + case IToken.t_dynamic_cast: + case IToken.t_static_cast: + case IToken.t_reinterpret_cast: + case IToken.t_const_cast: + case IToken.t_typeid: + return NO_TEMPLATE_ID; + + // Start of a primary expression + case IToken.tINTEGER: + case IToken.tFLOATINGPT: + case IToken.tSTRING: + case IToken.tLSTRING: + case IToken.tUTF16STRING: + case IToken.tUTF32STRING: + case IToken.tCHAR: + case IToken.tLCHAR: + case IToken.tUTF16CHAR: + case IToken.tUTF32CHAR: + case IToken.t_false: + case IToken.t_true: + case IToken.t_this: + case IToken.tIDENTIFIER: + case IToken.t_operator: + case IToken.tCOMPLETION: + return NO_TEMPLATE_ID; + + // Tokens that end an expression + case IToken.tSEMI: + case IToken.tCOMMA: + case IToken.tLBRACE: + case IToken.tRBRACE: + case IToken.tRBRACKET: + case IToken.tRPAREN: + case IToken.tELLIPSIS: // pack-expansion + case IToken.t_struct: + case IToken.t_class: + case IToken.t_template: + return TEMPLATE_ID; + + // Binary operator + case IToken.tGT: + case IToken.tGT_in_SHIFTR: + case IToken.tEQUAL: + return TEMPLATE_ID; + + default: + return AMBIGUOUS_TEMPLATE_ID; + } + } + + private List templateArgumentList(TemplateIdStrategy strat) throws EndOfFileException, BacktrackException { int startingOffset = LA(1).getOffset(); int endOffset = 0; List list= null; - final BinaryExprCtx exprCtx = isAmbiguous ? BinaryExprCtx.eAmbigTmplID : BinaryExprCtx.eTmplID; boolean needComma= false; int lt1= LT(1); @@ -532,7 +581,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { needComma= true; } - IASTNode node= templateArgument(exprCtx); + IASTNode node= templateArgument(strat); if (list == null) { list= new ArrayList(); } @@ -545,7 +594,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return list; } - private IASTNode templateArgument(BinaryExprCtx exprCtx) throws EndOfFileException, BacktrackException { + private IASTNode templateArgument(TemplateIdStrategy strat) throws EndOfFileException, BacktrackException { IToken argStart = mark(); ICPPASTTypeId typeId= null; int lt1= 0; @@ -567,7 +616,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { IToken typeIdEnd= mark(); IASTIdExpression idExpr= setRange(nodeFactory.newIdExpression(name), name); try { - IASTExpression expression = expression(ExprKind.eAssignment, exprCtx, idExpr); + IASTExpression expression = expression(ExprKind.eAssignment, BinaryExprCtx.eInTemplateID, idExpr, strat); boolean isAmbiguous= (expression == idExpr); if (LT(1) == IToken.tELLIPSIS) { IToken ellipsis= consume(); @@ -599,7 +648,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { // Not a type-id, parse as expression backup(argStart); - IASTExpression expr= expression(ExprKind.eAssignment, exprCtx, null); + IASTExpression expr= expression(ExprKind.eAssignment, BinaryExprCtx.eInTemplateID, null, strat); if (LT(1) == IToken.tELLIPSIS) { expr= addPackExpansion(expr, consume()); } @@ -679,76 +728,91 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { * template-id an ambiguous template-id (one where the '<' could be a greater sign) or * else where. */ - private enum BinaryExprCtx {eTmplID, eAmbigTmplID, eNoTmplID} + private enum BinaryExprCtx {eInTemplateID, eNotInTemplateID} @Override protected IASTExpression expression() throws BacktrackException, EndOfFileException { - return expression(ExprKind.eExpression, BinaryExprCtx.eNoTmplID, null); + return expression(ExprKind.eExpression, BinaryExprCtx.eNotInTemplateID, null, null); } @Override protected IASTExpression constantExpression() throws BacktrackException, EndOfFileException { - return expression(ExprKind.eConstant, BinaryExprCtx.eNoTmplID, null); + return expression(ExprKind.eConstant, BinaryExprCtx.eNotInTemplateID, null, null); } - private IASTExpression expression(final ExprKind kind, final BinaryExprCtx ctx, IASTInitializerClause expr) throws EndOfFileException, BacktrackException { + private IASTExpression expression(final ExprKind kind, final BinaryExprCtx ctx, IASTInitializerClause expr, TemplateIdStrategy strat) throws EndOfFileException, BacktrackException { final boolean allowComma= kind==ExprKind.eExpression; boolean allowAssignment= kind !=ExprKind.eConstant; - final CastExprCtx castCtx= ctx == BinaryExprCtx.eNoTmplID ? CastExprCtx.eBExpr : CastExprCtx.eBExprInTmplID; if (allowAssignment && LT(1) == IToken.t_throw) { return throwExpression(); } + final boolean handleVariants= strat == null; + if (handleVariants) { + strat= new TemplateIdStrategy(); + } + + + final int startOffset= expr != null ? ((ASTNode) expr).getOffset() : LA(1).getOffset(); int lt1; int conditionCount= 0; BinaryOperator lastOperator= null; - if (expr == null) { - expr= castExpression(castCtx); + NameOrTemplateIDVariants variants= null; + + if (expr == null) { + Object e = castExpressionForBinaryExpression(handleVariants, strat); + if (e instanceof IASTExpression) { + expr= (IASTExpression) e; + } else { + variants= new NameOrTemplateIDVariants(); + + final Variant variant = (Variant) e; + expr= variant.getExpression(); + variants.addBranchPoint(variant.getNext(), lastOperator, allowAssignment, conditionCount); + } } - loop: while(true) { + boolean doneExpression= false; + do { // Typically after a binary operator there cannot be a throw expression boolean allowThrow= false; // Brace initializers are allowed on the right hand side of an expression boolean allowBraceInitializer= false; + + BacktrackException tryRecovery= null; + final int operatorOffset= LA().getOffset(); lt1= LT(1); switch (lt1) { case IToken.tQUESTION: - if (ctx == BinaryExprCtx.eAmbigTmplID) { - throwBacktrack(LA(1)); - } conditionCount++; // ? : // Precedence: 25 is lower than precedence of logical or; 0 is lower than precedence of expression lastOperator= new BinaryOperator(lastOperator, expr, lt1, 25, 0); - if (LT(2) == IToken.tCOLON) { - // Gnu extension: The expression after '?' can be omitted. - consume(); // Consume operator - expr= null; // Next cast expression is just null - continue; - } allowAssignment= true; // assignment expressions will be subsumed by the conditional expression allowThrow= true; break; case IToken.tCOLON: - if (--conditionCount < 0) - break loop; - - // ? : - // Precedence: 0 is lower than precedence of expression; 15 is lower than precedence of assignment; - lastOperator= new BinaryOperator(lastOperator, expr, lt1, 0, 15); - allowAssignment= true; // assignment expressions will be subsumed by the conditional expression - allowThrow= true; + if (--conditionCount < 0) { + doneExpression= true; + } else { + // ? : + // Precedence: 0 is lower than precedence of expression; 15 is lower than precedence of assignment; + lastOperator= new BinaryOperator(lastOperator, expr, lt1, 0, 15); + allowAssignment= true; // assignment expressions will be subsumed by the conditional expression + allowThrow= true; + } break; case IToken.tCOMMA: allowThrow= true; - if (!allowComma && conditionCount == 0) - break loop; - // Lowest precedence except inside the conditional expression - lastOperator= new BinaryOperator(lastOperator, expr, lt1, 10, 11); + if (!allowComma && conditionCount == 0) { + doneExpression= true; + } else { + // Lowest precedence except inside the conditional expression + lastOperator= new BinaryOperator(lastOperator, expr, lt1, 10, 11); + } break; case IToken.tASSIGN: @@ -762,23 +826,19 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.tAMPERASSIGN: case IToken.tXORASSIGN: case IToken.tBITORASSIGN: - if (!allowAssignment && conditionCount == 0) - break loop; - // Assignments group right to left - lastOperator= new BinaryOperator(lastOperator, expr, lt1, 21, 20); - allowBraceInitializer= true; + if (!allowAssignment && conditionCount == 0) { + doneExpression= true; + } else { + // Assignments group right to left + lastOperator= new BinaryOperator(lastOperator, expr, lt1, 21, 20); + allowBraceInitializer= true; + } break; case IToken.tOR: - if (ctx == BinaryExprCtx.eAmbigTmplID) { - throwBacktrack(LA(1)); - } lastOperator= new BinaryOperator(lastOperator, expr, lt1, 30, 31); break; case IToken.tAND: - if (ctx == BinaryExprCtx.eAmbigTmplID) { - throwBacktrack(LA(1)); - } lastOperator= new BinaryOperator(lastOperator, expr, lt1, 40, 41); break; case IToken.tBITOR: @@ -795,8 +855,10 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { lastOperator= new BinaryOperator(lastOperator, expr, lt1, 80, 81); break; case IToken.tGT: - if (ctx != BinaryExprCtx.eNoTmplID) - break loop; + if (ctx == BinaryExprCtx.eInTemplateID) { + doneExpression= true; + break; + } //$FALL-THROUGH$ case IToken.tLT: case IToken.tLTEQUAL: @@ -806,11 +868,17 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { lastOperator= new BinaryOperator(lastOperator, expr, lt1, 90, 91); break; case IToken.tGT_in_SHIFTR: - if (ctx != BinaryExprCtx.eNoTmplID) - break loop; + if (ctx == BinaryExprCtx.eInTemplateID) { + doneExpression= true; + break; + } if (LT(2) != IToken.tGT_in_SHIFTR) { - throwBacktrack(LA(1)); + IToken token = LA(1); + backtrack.initialize(token.getOffset(), token.getLength()); + tryRecovery= backtrack; + break; } + lt1= IToken.tSHIFTR; // convert back consume(); // consume the extra token //$FALL-THROUGH$ @@ -832,32 +900,150 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { lastOperator= new BinaryOperator(lastOperator, expr, lt1, 130, 131); break; default: - break loop; - } - - consume(); // consume operator - if (allowThrow && LT(1) == IToken.t_throw) { - expr= throwExpression(); - } else if (allowBraceInitializer && LT(1) == IToken.tLBRACE) { - expr= bracedInitList(true); - } else { - expr= castExpression(castCtx); // next cast expression - continue loop; + doneExpression= true; + break; } - // For throw and braced initializers we require that the assignment expression ends. - lt1= LT(1); - if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA) - break loop; - } + if (!doneExpression && tryRecovery == null) { + consume(); // consumes the operator + + // Link variants that are closed by the new operator + if (variants != null) { + variants.closeVariants(operatorOffset, lastOperator); + } + + // Determine next sub-expression + if (lt1 == IToken.tQUESTION && LT(1) == IToken.tCOLON) { + // Missing sub-expression after '?' (gnu-extension) + expr= null; + } else if (allowThrow && LT(1) == IToken.t_throw) { + // Throw expression + expr= throwExpression(); + lt1= LT(1); + if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA) + break; + } else if (allowBraceInitializer && LT(1) == IToken.tLBRACE) { + // Brace initializer + expr= bracedInitList(true); + lt1= LT(1); + if (lt1 != IToken.tCOLON && lt1 != IToken.tCOMMA) + break; + } else { + // Cast expression + IToken m= mark(); + try { + Object e = castExpressionForBinaryExpression(handleVariants, strat); + if (e instanceof IASTExpression) { + expr= (IASTExpression) e; + } else { + final Variant ae = (Variant) e; + expr= ae.getExpression(); + if (variants == null) + variants= new NameOrTemplateIDVariants(); + + variants.addBranchPoint(ae.getNext(), lastOperator, allowAssignment, conditionCount); + } + } catch (BacktrackException e) { + if (variants == null) + throw e; + tryRecovery= e; + backup(m); + } + } + } + + if (tryRecovery != null || doneExpression) { + if (variants != null) { + if (lt1 == IToken.tEOC) { + variants.discardOpenVariants(operatorOffset); + } else { + // Try fall-back to an open variant + Variant fallback= variants.findFallback(operatorOffset); + if (fallback == null) { + if (tryRecovery != null) + throw tryRecovery; + variants.discardOpenVariants(operatorOffset); + } else { + // Restore state and continue + doneExpression= false; + BranchPoint varPoint= fallback.getOwner(); + allowAssignment= varPoint.isAllowAssignment(); + conditionCount= varPoint.getConditionCount(); + lastOperator= varPoint.getLeftOperator(); + expr= fallback.getExpression(); + variants.useFallback(fallback); + + // Advance to the right token + int offset= fallback.getRightOffset(); + while (LA().getOffset() < offset) { + consume(); + } + } + } + } + } + } while (!doneExpression); // Check for incomplete conditional expression if (lt1 != IToken.tEOC && conditionCount > 0) throwBacktrack(LA(1)); + if (variants != null && !variants.isEmpty()) { + CPPASTTemplateIDAmbiguity result = new CPPASTTemplateIDAmbiguity(this, lastOperator, expr, variants.getOrderedBranchPoints()); + setRange(result, startOffset, calculateEndOffset(expr)); + return result; + } + return buildExpression(lastOperator, expr); } + public Object castExpressionForBinaryExpression(boolean handleVariants, final TemplateIdStrategy strat) + throws EndOfFileException, BacktrackException { + if (!handleVariants) + return castExpression(CastExprCtx.eDirectlyInBExpr, strat); + + Variant variants= null; + IASTExpression singleExpression= null; + IASTName[] firstNames= null; + + final IToken mark= mark(); + IToken lastToken= null; + strat.reset(); + for(;;) { + try { + IASTExpression e = castExpression(CastExprCtx.eDirectlyInBExpr, strat); + if (variants == null) { + if (singleExpression == null || lastToken == null) { + singleExpression= e; + firstNames= strat.getTemplateNames(); + } else { + variants= new Variant(null, singleExpression, firstNames, lastToken.getOffset()); + singleExpression= null; + firstNames= null; + } + } + lastToken= LA(); + if (variants != null) { + variants = new Variant(variants, e, strat.getTemplateNames(), lastToken.getOffset()); + } + if (!strat.setNextAlternative()) { + break; + } + } catch (BacktrackException e) { + if (!strat.setNextAlternative()) { + if (lastToken == null) + throw e; + + backup(lastToken); + break; + } + } + backup(mark); + } + return variants != null ? variants : singleExpression; + } + + @Override protected IASTExpression buildBinaryExpression(int operator, IASTExpression expr1, IASTInitializerClause expr2, int lastOffset) { IASTBinaryExpression result = nodeFactory.newBinaryExpression(operator, expr1, expr2); @@ -899,7 +1085,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { consume(IToken.tRBRACKET); vectored = true; } - IASTExpression castExpression = castExpression(CastExprCtx.eNotBExpr); + IASTExpression castExpression = castExpression(CastExprCtx.eNotInBExpr, null); ICPPASTDeleteExpression deleteExpression = nodeFactory.newDeleteExpression(castExpression); ((ASTNode) deleteExpression).setOffsetAndLength(startingOffset, calculateEndOffset(castExpression) - startingOffset); deleteExpression.setIsGlobal(global); @@ -1051,24 +1237,24 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { @Override - protected IASTExpression unaryExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected IASTExpression unaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { switch (LT(1)) { case IToken.tSTAR: - return unaryExpression(IASTUnaryExpression.op_star, ctx); + return unaryExpression(IASTUnaryExpression.op_star, ctx, strat); case IToken.tAMPER: - return unaryExpression(IASTUnaryExpression.op_amper, ctx); + return unaryExpression(IASTUnaryExpression.op_amper, ctx, strat); case IToken.tPLUS: - return unaryExpression(IASTUnaryExpression.op_plus, ctx); + return unaryExpression(IASTUnaryExpression.op_plus, ctx, strat); case IToken.tMINUS: - return unaryExpression(IASTUnaryExpression.op_minus, ctx); + return unaryExpression(IASTUnaryExpression.op_minus, ctx, strat); case IToken.tNOT: - return unaryExpression(IASTUnaryExpression.op_not, ctx); + return unaryExpression(IASTUnaryExpression.op_not, ctx, strat); case IToken.tBITCOMPLEMENT: - return unaryExpression(IASTUnaryExpression.op_tilde, ctx); + return unaryExpression(IASTUnaryExpression.op_tilde, ctx, strat); case IToken.tINCR: - return unaryExpression(IASTUnaryExpression.op_prefixIncr, ctx); + return unaryExpression(IASTUnaryExpression.op_prefixIncr, ctx, strat); case IToken.tDECR: - return unaryExpression(IASTUnaryExpression.op_prefixDecr, ctx); + return unaryExpression(IASTUnaryExpression.op_prefixDecr, ctx, strat); case IToken.t_new: return newExpression(); case IToken.t_delete: @@ -1080,7 +1266,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.t_delete: return deleteExpression(); default: - return postfixExpression(ctx); + return postfixExpression(ctx, strat); } case IToken.t_sizeof: if (LTcatchEOF(2) == IToken.tELLIPSIS) { @@ -1101,12 +1287,12 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return expr; } return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), - IASTTypeIdExpression.op_sizeof, IASTUnaryExpression.op_sizeof, ctx); + IASTTypeIdExpression.op_sizeof, IASTUnaryExpression.op_sizeof, ctx, strat); case IGCCToken.t___alignof__: return parseTypeidInParenthesisOrUnaryExpression(false, consume().getOffset(), - IASTTypeIdExpression.op_alignof, IASTUnaryExpression.op_alignOf, ctx); + IASTTypeIdExpression.op_alignof, IASTUnaryExpression.op_alignOf, ctx, strat); default: - return postfixExpression(ctx); + return postfixExpression(ctx, strat); } } @@ -1137,7 +1323,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { * typeid ( expression ) * typeid ( type-id ) */ - private IASTExpression postfixExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + private IASTExpression postfixExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { IASTExpression firstExpression = null; boolean isTemplate = false; @@ -1160,7 +1346,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { // 'typeid' ( type-id ) int so = consume().getOffset(); firstExpression = parseTypeidInParenthesisOrUnaryExpression(true, so, - ICPPASTTypeIdExpression.op_typeid, ICPPASTUnaryExpression.op_typeid, ctx); + ICPPASTTypeIdExpression.op_typeid, ICPPASTUnaryExpression.op_typeid, ctx, strat); break; case IToken.tLPAREN: @@ -1181,7 +1367,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } catch (BacktrackException bt) { } backup(m); - firstExpression= primaryExpression(ctx); + firstExpression= primaryExpression(ctx, strat); break; // typename-specifier ( expression-list_opt ) @@ -1208,7 +1394,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { break; default: - firstExpression = primaryExpression(ctx); + firstExpression = primaryExpression(ctx, strat); if (firstExpression instanceof IASTIdExpression && LT(1) == IToken.tLBRACE) { IASTName name = ((IASTIdExpression) firstExpression).getName(); ICPPASTDeclSpecifier declSpec= nodeFactory.newTypedefNameSpecifier(name); @@ -1269,7 +1455,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { isTemplate = true; } - IASTName name = qualifiedName(ctx); + IASTName name = qualifiedName(strat, ctx); if (name == null) throwBacktrack(((ASTNode) firstExpression).getOffset(), @@ -1292,7 +1478,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { isTemplate = true; } - name = qualifiedName(ctx); + name = qualifiedName(strat, ctx); if (name == null) throwBacktrack(((ASTNode) firstExpression).getOffset(), @@ -1340,7 +1526,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } @Override - protected IASTExpression primaryExpression(CastExprCtx ctx) throws EndOfFileException, BacktrackException { + protected IASTExpression primaryExpression(CastExprCtx ctx, ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException { IToken t = null; IASTLiteralExpression literalExpression = null; switch (LT(1)) { @@ -1390,7 +1576,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } t = consume(); int finalOffset= 0; - IASTExpression lhs= expression(ExprKind.eExpression, BinaryExprCtx.eNoTmplID, null); // instead of expression(), to keep the stack smaller + IASTExpression lhs= expression(ExprKind.eExpression, BinaryExprCtx.eNotInTemplateID, null, null); // instead of expression(), to keep the stack smaller switch (LT(1)) { case IToken.tRPAREN: case IToken.tEOC: @@ -1405,7 +1591,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.t_operator: case IToken.tCOMPLETION: case IToken.tBITCOMPLEMENT: { - IASTName name = qualifiedName(ctx); + IASTName name = qualifiedName(strat, ctx); IASTIdExpression idExpression = nodeFactory.newIdExpression(name); ((ASTNode) idExpression).setOffsetAndLength(((ASTNode) name).getOffset(), ((ASTNode) name).getOffset() + ((ASTNode) name).getLength() - ((ASTNode) name).getOffset()); @@ -1568,7 +1754,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { case IToken.tIDENTIFIER: case IToken.tCOLONCOLON: case IToken.tCOMPLETION: - name = qualifiedName(CastExprCtx.eNotBExpr); + name = qualifiedName(); break; default: throwBacktrack(offset, endOffset - offset); @@ -1599,7 +1785,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { consume(); } - IASTName name = qualifiedName(CastExprCtx.eNotBExpr); + IASTName name = qualifiedName(); int end; switch (LT(1)) { case IToken.tSEMI: @@ -1722,7 +1908,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return templateSpecialization; } - List parms= templateParameterList(); + List parms= outerTemplateParameterList(); consume(IToken.tGT, IToken.tGT_in_SHIFTR); IASTDeclaration d = declaration(option); ICPPASTTemplateDeclaration templateDecl = nodeFactory.newTemplateDeclaration(d); @@ -1750,25 +1936,47 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { * @throws BacktrackException * request for a backtrack */ - protected List templateParameterList() throws BacktrackException, EndOfFileException { - List result = new ArrayList(DEFAULT_PARM_LIST_SIZE); - boolean needComma= false; - for (;;) { - final int lt1= LT(1); - if (lt1 == IToken.tGT || lt1 == IToken.tEOC || lt1 == IToken.tGT_in_SHIFTR) - return result; - - if (needComma) { - consume(IToken.tCOMMA); - } else { - needComma= true; - } - - ICPPASTTemplateParameter tpar= templateParameter(); - result.add(tpar); - } + protected List outerTemplateParameterList() throws BacktrackException, EndOfFileException { + fTemplateParameterListStrategy= new TemplateIdStrategy(); + try { + List result = new ArrayList(DEFAULT_PARM_LIST_SIZE); + IToken m= mark(); + for(;;) { + try { + return templateParameterList(result); + } catch (BacktrackException e) { + if (!fTemplateParameterListStrategy.setNextAlternative()) { + fTemplateParameterListStrategy= null; + throw e; + } + result.clear(); + backup(m); + } + } + } finally { + fTemplateParameterListStrategy= null; + } } + private List templateParameterList(List result) + throws EndOfFileException, BacktrackException { + boolean needComma= false; + for (;;) { + final int lt1= LT(1); + if (lt1 == IToken.tGT || lt1 == IToken.tEOC || lt1 == IToken.tGT_in_SHIFTR) { + return result; + } + + if (needComma) { + consume(IToken.tCOMMA); + } else { + needComma= true; + } + + result.add(templateParameter()); + } + } + private ICPPASTTemplateParameter templateParameter() throws EndOfFileException, BacktrackException { final int lt1= LT(1); final IToken start= mark(); @@ -1821,7 +2029,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { consume(); consume(IToken.tLT); - List tparList = templateParameterList(); + List tparList = templateParameterList(new ArrayList()); consume(IToken.tGT, IToken.tGT_in_SHIFTR); int endOffset = consume(IToken.t_class).getEndOffset(); @@ -1838,7 +2046,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { throw backtrack; consume(); - defaultValue = primaryExpression(CastExprCtx.eNotBExpr); + defaultValue = primaryExpression(CastExprCtx.eNotInBExpr, null); endOffset = calculateEndOffset(defaultValue); } } else { @@ -1857,13 +2065,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } // Try non-type template parameter - boolean inTParList= fInTemplateParameterList; - try { - fInTemplateParameterList= true; - return parameterDeclaration(); - } finally { - fInTemplateParameterList= inTParList; - } + return parameterDeclaration(); } @@ -1991,7 +2193,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return null; } - IASTName qualifiedName= qualifiedName(CastExprCtx.eNotBExpr); + IASTName qualifiedName= qualifiedName(); endOffset = consume(IToken.tSEMI).getEndOffset(); ICPPASTNamespaceAlias alias = nodeFactory.newNamespaceAlias(name, qualifiedName); @@ -2230,7 +2432,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { consume(IToken.tCOLON); loop: for(;;) { final int offset= LA(1).getOffset(); - final IASTName name = qualifiedName(CastExprCtx.eNotBExpr); + final IASTName name = qualifiedName(); final IASTInitializer init; int endOffset; if (LT(1) != IToken.tEOC) { @@ -2520,7 +2722,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { if (encounteredTypename || encounteredRawType) break declSpecifiers; consume(); - identifier= qualifiedName(CastExprCtx.eNotBExpr); + identifier= qualifiedName(); endOffset= calculateEndOffset(identifier); isTypename = true; encounteredTypename= true; @@ -2539,7 +2741,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } } - identifier= qualifiedName(CastExprCtx.eNotBExpr); + identifier= qualifiedName(); if (identifier.getLookupKey().length == 0 && LT(1) != IToken.tEOC) throwBacktrack(LA(1)); @@ -2595,7 +2797,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { simpleType= IASTSimpleDeclSpecifier.t_typeof; consume(IGCCToken.t_typeof); typeofExpression= parseTypeidInParenthesisOrUnaryExpression(false, LA(1).getOffset(), - IASTTypeIdExpression.op_typeof, -1, CastExprCtx.eNotBExpr); + IASTTypeIdExpression.op_typeof, -1, CastExprCtx.eNotInBExpr, null); encounteredTypename= true; endOffset= calculateEndOffset(typeofExpression); @@ -2800,7 +3002,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { // if __attribute__ or __declspec occurs after struct/union/class and before the identifier __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers); - IASTName name = qualifiedName(CastExprCtx.eNotBExpr); + IASTName name = qualifiedName(); return setRange(nodeFactory.newElaboratedTypeSpecifier(eck, name), offset, calculateEndOffset(name)); } @@ -3107,8 +3309,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } // assignment expression - final BinaryExprCtx ctx = fInTemplateParameterList ? BinaryExprCtx.eTmplID : BinaryExprCtx.eNoTmplID; - IASTExpression assignmentExpression = expression(ExprKind.eAssignment, ctx, null); + TemplateIdStrategy strat= fTemplateParameterListStrategy; + final BinaryExprCtx ctx= strat != null ? BinaryExprCtx.eInTemplateID : BinaryExprCtx.eNotInTemplateID; + IASTExpression assignmentExpression = expression(ExprKind.eAssignment, ctx, null, strat); if (allowSkipping && skipTrivialExpressionsInAggregateInitializers) { if (!ASTQueries.canContainName(assignmentExpression)) return null; @@ -3255,7 +3458,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { if (option.fRequireAbstract) throwBacktrack(LA(1)); - final IASTName declaratorName= !option.fRequireSimpleName ? qualifiedName(CastExprCtx.eNotBExpr) : identifier(); + final IASTName declaratorName= !option.fRequireSimpleName ? qualifiedName() : identifier(); endOffset= calculateEndOffset(declaratorName); return declarator(pointerOps, hasEllipsis, declaratorName, null, startingOffset, endOffset, strategy, option); } @@ -3373,7 +3576,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } if (coloncolon != 0) { try { - name= qualifiedName(CastExprCtx.eNotBExpr); + name= qualifiedName(); if (name.getLookupKey().length != 0) { backup(mark); return result; @@ -3704,7 +3907,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { // class name IASTName name = null; if (LT(1) == IToken.tIDENTIFIER) - name = qualifiedName(CastExprCtx.eNotBExpr); + name = qualifiedName(); else name = nodeFactory.newName(); @@ -3814,7 +4017,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { break loop; } } - name = qualifiedName(CastExprCtx.eNotBExpr); + name = qualifiedName(); ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier baseSpec = nodeFactory.newBaseSpecifier(name, visibility, isVirtual); setRange(baseSpec, startOffset, calculateEndOffset(name)); return baseSpec; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/NameOrTemplateIDVariants.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/NameOrTemplateIDVariants.java new file mode 100644 index 00000000000..6bc530b875d --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/NameOrTemplateIDVariants.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2011 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 org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.BinaryOperator; + +/** + * Tracks variants of expressions due to the ambiguity between template-id and '<' operator. + */ +public class NameOrTemplateIDVariants { + /** + * A point where a '<' can be interpreted as less-than or as the angle-bracket of a template-id. + */ + static class BranchPoint { + private BranchPoint fNext; + private Variant fFirstVariant; + private final boolean fAllowAssignment; + private final int fConditionCount; + private final BinaryOperator fLeftOperator; + + BranchPoint(BranchPoint next, Variant variant, + BinaryOperator left, boolean allowAssignment, int conditionCount) { + fNext= next; + fFirstVariant= variant; + fAllowAssignment= allowAssignment; + fConditionCount= conditionCount; + fLeftOperator= left; + // Set owner + while (variant != null) { + variant.fOwner= this; + variant= variant.getNext(); + } + } + + public boolean isAllowAssignment() { + return fAllowAssignment; + } + public int getConditionCount() { + return fConditionCount; + } + public BinaryOperator getLeftOperator() { + return fLeftOperator; + } + public Variant getFirstVariant() { + return fFirstVariant; + } + public BranchPoint getNext() { + return fNext; + } + + public void reverseVariants() { + Variant prev= null; + Variant curr= fFirstVariant; + while (curr != null) { + Variant next= curr.getNext(); + curr.fNext= prev; + prev= curr; + curr= next; + } + fFirstVariant= prev; + } + } + + /** + * A variant for a branch-point is a cast-expression that can be used within a binary expression. + */ + static class Variant { + private BranchPoint fOwner; + private Variant fNext; + private final IASTExpression fExpression; + private BinaryOperator fTargetOperator; + private final int fRightOffset; + private final IASTName[] fTemplateNames; + + public Variant(Variant next, IASTExpression expr, IASTName[] templateNames, int rightOffset) { + fNext= next; + fExpression= expr; + fRightOffset= rightOffset; + fTemplateNames= templateNames; + } + + public BranchPoint getOwner() { + return fOwner; + } + public int getRightOffset() { + return fRightOffset; + } + public IASTName[] getTemplateNames() { + return fTemplateNames; + } + public Variant getNext() { + return fNext; + } + public IASTExpression getExpression() { + return fExpression; + } + public BinaryOperator getTargetOperator() { + return fTargetOperator; + } + public void setTargetOperator(BinaryOperator lastOperator) { + fTargetOperator= lastOperator; + } + } + + + private BranchPoint fFirst; + + public boolean isEmpty() { + return fFirst == null; + } + + public void addBranchPoint(Variant variants, BinaryOperator left, + boolean allowAssignment, int conditionCount) { + fFirst= new BranchPoint(fFirst, variants, left, allowAssignment, conditionCount); + } + + public void closeVariants(int offset, BinaryOperator lastOperator) { + for (BranchPoint p = fFirst; p != null; p= p.getNext()) { + for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) { + if (v.getTargetOperator() == null) { + if (offset == v.getRightOffset()) { + v.setTargetOperator(lastOperator); + } else if (offset > v.getRightOffset()) { + // Should not happen + assert false; + remove(v); + } + } + } + } + } + + public void discardOpenVariants(int operatorOffset) { + for (BranchPoint p = fFirst; p != null; p= p.getNext()) { + for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) { + if (v.getTargetOperator() == null && v.getRightOffset() != operatorOffset) { + remove(v); + } + } + } + } + + public Variant findFallback(int operatorOffset) { + // Search for an open variant, with a small right offset and a large left offset + Variant best= null; + for (BranchPoint p = fFirst; p != null; p= p.getNext()) { + for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) { + if (v.fRightOffset > operatorOffset) { + if (best == null || v.fRightOffset < best.fRightOffset) { + best= v; + } + } + } + } + return best; + } + + public void useFallback(Variant fallback) { + // Discard variants that end within the fallback + int begin= ((ASTNode) fallback.getExpression()).getOffset(); + int end= fallback.getRightOffset(); + for (BranchPoint p = fFirst; p != null; p= p.getNext()) { + for (Variant v= p.getFirstVariant(); v != null; v= v.getNext()) { + if (v == fallback) { + remove(v); + } else { + int vend= v.getRightOffset(); + if (vend > begin && vend < end) + remove(v); + } + } + } + } + + + private void remove(Variant remove) { + final BranchPoint owner = remove.fOwner; + final Variant next = remove.getNext(); + Variant prev= owner.getFirstVariant(); + if (remove == prev) { + owner.fFirstVariant= next; + if (next == null) { + remove(owner); + } + } else { + while (prev != null) { + Variant n = prev.getNext(); + if (n == remove) { + prev.fNext= next; + break; + } + prev= n; + } + } + } + + private void remove(BranchPoint remove) { + final BranchPoint next = remove.getNext(); + if (remove == fFirst) { + fFirst= next; + } else { + BranchPoint prev= fFirst; + while (prev != null) { + BranchPoint n = prev.getNext(); + if (n == remove) { + prev.fNext= next; + break; + } + prev= n; + } + } + } + + public BranchPoint getOrderedBranchPoints() { + BranchPoint prev= null; + BranchPoint curr= fFirst; + while (curr != null) { + curr.reverseVariants(); + BranchPoint next= curr.getNext(); + curr.fNext= prev; + prev= curr; + curr= next; + } + fFirst= null; + return prev; + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/TemplateIdStrategy.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/TemplateIdStrategy.java new file mode 100644 index 00000000000..11243a01cad --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/TemplateIdStrategy.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2011 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.BitSet; + +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.parser.util.ArrayUtil; +import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.ITemplateIdStrategy; + +/** + * Governs backtracking through multiple variants due to the ambiguous meaning of '<'. + * @see NameOrTemplateIDVariants + */ +final class TemplateIdStrategy implements ITemplateIdStrategy { + private int fCurrentBranchPoint; + private BitSet fSimpleIDs; + private IASTName[] fTemplateNames; + + public TemplateIdStrategy() { + reset(); + } + + public void reset() { + fCurrentBranchPoint= -1; + fTemplateNames= IASTName.EMPTY_NAME_ARRAY; + if (fSimpleIDs != null) { + fSimpleIDs.clear(); + } + } + + public boolean ignoreTemplateID() { + fCurrentBranchPoint++; + return fSimpleIDs == null ? false : fSimpleIDs.get(fCurrentBranchPoint); + } + + public void addTemplateName(IASTName name) { + fTemplateNames= ArrayUtil.append(fTemplateNames, name); + } + + public boolean setNextAlternative() { + final int bp = fCurrentBranchPoint; + if (bp < 0) + return false; + + fCurrentBranchPoint= -1; + fTemplateNames= IASTName.EMPTY_NAME_ARRAY; + if (fSimpleIDs == null) { + fSimpleIDs= new BitSet(); + } + + // Set a new branch as far right as possible. + final int len = fSimpleIDs.length(); + if (len <= bp) { + fSimpleIDs.set(bp); + return true; + } + + for (int branch= Math.min(bp, len-2); branch>=0; branch--) { + if (!fSimpleIDs.get(branch)) { + fSimpleIDs.clear(branch+1, len); + fSimpleIDs.set(branch); + return true; + } + } + return false; + } + + public IASTName[] getTemplateNames() { + return ArrayUtil.trim(fTemplateNames); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java index e50cbb37308..679648898e6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java @@ -1069,8 +1069,11 @@ public class CPPVisitor extends ASTQueries { if (qname.isFullyQualified()) { return parent.getTranslationUnit().getScope(); } - } - if (i > 0) { + if (qname.getParent() instanceof ICPPASTFieldReference) { + name= qname; + parent= name.getParent(); + } + } else if (i > 0) { if (data != null) { data.usesEnclosingScope= false; } @@ -1111,7 +1114,9 @@ public class CPPVisitor extends ASTQueries { return scope; } } - } else if (parent instanceof ICPPASTFieldReference) { + } + + if (parent instanceof ICPPASTFieldReference) { if (data != null) { data.usesEnclosingScope= false; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java index 1b07da002d3..37e71541d08 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 IBM Corporation and others. + * 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 @@ -251,15 +251,27 @@ public class LookupData { } public boolean qualified() { - if (forceQualified) return true; - if (astName == null) return false; - if (astName.getPropertyInParent() == CPPSemantics.STRING_LOOKUP_PROPERTY) return false; - IASTNode p1 = astName.getParent(); - if (p1 instanceof ICPPASTQualifiedName) { - final IASTName[] qnames = ((ICPPASTQualifiedName) p1).getNames(); - return qnames.length == 1 || qnames[0] != astName; + if (forceQualified) + return true; + + IASTName n= astName; + if (n == null || n.getPropertyInParent() == CPPSemantics.STRING_LOOKUP_PROPERTY) + return false; + + IASTNode p = n.getParent(); + if (p instanceof ICPPASTTemplateId) { + n= (IASTName) p; + p= p.getParent(); } - return p1 instanceof ICPPASTFieldReference; + if (p instanceof ICPPASTQualifiedName) { + final ICPPASTQualifiedName qname = (ICPPASTQualifiedName) p; + if (qname.isFullyQualified()) + return true; + final IASTName[] qnames = qname.getNames(); + if (qnames.length > 0 && qnames[0] != n) + return true; + } + return p instanceof ICPPASTFieldReference; } public boolean isFunctionCall() { diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java index 36fa9388ac0..79ae7ebc54c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AddIncludeTest.java @@ -100,7 +100,7 @@ public class AddIncludeTest extends TestCase { String file= createFileName(".expected"); String expected= ResourceTestHelper.read(file).toString(); - assertEquals(expected, fDocument.get()); + assertEquals(expected.replace("\r\n", "\n"), fDocument.get().replace("\r\n", "\n")); } private String createFileName(String suffix) {