From dd5413a3a283d41ade2939ea03da76dfe184fd51 Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Tue, 14 Oct 2008 09:29:23 +0000 Subject: [PATCH] Information about syntax between ast-nodes, bug 250251. --- .../cdt/core/parser/tests/ast2/AST2Tests.java | 137 +++++++++++++ .../tests/scanner/LocationMapTests.java | 3 +- .../eclipse/cdt/core/dom/ast/ASTVisitor.java | 32 +++ .../ExpansionOverlapsBoundaryException.java | 19 ++ .../eclipse/cdt/core/dom/ast/IASTNode.java | 50 ++++- .../cdt/internal/core/dom/parser/ASTNode.java | 107 +++++++++- .../core/dom/parser/ASTNodeSearch.java | 191 ++++++++++++++++++ .../core/dom/parser/ASTTranslationUnit.java | 4 + .../core/dom/rewrite/ASTLiteralNode.java | 9 + .../parser/scanner/ASTPreprocessorNode.java | 11 + .../core/parser/scanner/CPreprocessor.java | 3 +- .../parser/scanner/ILocationResolver.java | 13 ++ .../core/parser/scanner/LocationCtx.java | 8 + .../parser/scanner/LocationCtxContainer.java | 15 ++ .../core/parser/scanner/LocationMap.java | 18 +- .../scanner/MultiMacroExpansionExplorer.java | 4 +- .../scanner/SingleMacroExpansionExplorer.java | 15 +- .../core/pdom/dom/PDOMASTAdapter.java | 12 ++ 18 files changed, 632 insertions(+), 19 deletions(-) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ExpansionOverlapsBoundaryException.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNodeSearch.java 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 eaab8a61819..2eca9e19471 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 @@ -18,6 +18,7 @@ import junit.framework.TestSuite; import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; +import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; @@ -28,6 +29,7 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTDoStatement; import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement; @@ -98,6 +100,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; +import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.core.parser.ParserLanguage; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.c.CFunction; @@ -5292,4 +5295,138 @@ public class AST2Tests extends AST2BaseTest { parseAndCheckBindings(getAboveComment(), ParserLanguage.C, true); parseAndCheckBindings(getAboveComment(), ParserLanguage.CPP, true); } + + // #define IF if + // #define IF_P if ( + // #define IF_P_T if (1 + // #define SEMI_IF ; if + // #define IF_COND if (1) + // void test() { + public void testLeadingSyntax_Bug250251() throws Exception { + String code= getAboveComment(); + + IASTTranslationUnit tu= parseAndCheckBindings(code + "if (1) {};}"); + IASTFunctionDefinition f= getDeclaration(tu, 0); + IASTIfStatement i = getStatement(f, 0); + IASTExpression x= i.getConditionExpression(); + IToken syntax= x.getLeadingSyntax(); + checkToken(syntax, "if", -4); syntax= syntax.getNext(); + checkToken(syntax, "(", -1); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "if( 1) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + syntax= x.getLeadingSyntax(); + checkToken(syntax, "if", -5); syntax= syntax.getNext(); + checkToken(syntax, "(", -3); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "if(1) ; else ;}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); IASTStatement est= i.getElseClause(); + syntax= est.getLeadingSyntax(); + checkToken(syntax, "else", -5); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "IF(1) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + syntax= x.getLeadingSyntax(); + checkToken(syntax, "IF", -3); syntax= syntax.getNext(); + checkToken(syntax, "(", -1); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "IF_P 1) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + syntax= x.getLeadingSyntax(); + checkToken(syntax, "IF_P", -5); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "IF_P_T ) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + try { + syntax= x.getLeadingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + + tu= parseAndCheckBindings(code + "SEMI_IF (1) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 1); x= i.getConditionExpression(); + try { + syntax= x.getLeadingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + + tu= parseAndCheckBindings(code + "IF_COND {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + try { + syntax= x.getLeadingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + } + + // #define P(x) ) + // #define BLOCK() {} + // #define T_P 1) + // #define P_BLOCK ){} + // #define IF_COND if (1) + // void test() { + public void testTrailingSyntax_Bug250251() throws Exception { + String code= getAboveComment(); + + IASTTranslationUnit tu= parseAndCheckBindings(code + "if (1) {};}"); + IASTFunctionDefinition f= getDeclaration(tu, 0); + IASTIfStatement i = getStatement(f, 0); + IASTExpression x= i.getConditionExpression(); + IToken syntax= x.getTrailingSyntax(); + checkToken(syntax, ")", 0); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "do {} while(1 );}"); + f= getDeclaration(tu, 0); IASTDoStatement dstmt= getStatement(f, 0); x= dstmt.getCondition(); + syntax= x.getTrailingSyntax(); + checkToken(syntax, ")", 1); syntax= syntax.getNext(); + checkToken(syntax, ";", 2); syntax= syntax.getNext(); + assertNull(syntax); + + + tu= parseAndCheckBindings(code + "if(1 ) BLOCK()}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + syntax= x.getTrailingSyntax(); + checkToken(syntax, ")", 1); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "if(1 P(0) {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + syntax= x.getTrailingSyntax(); + checkToken(syntax, "P", 1); syntax= syntax.getNext(); + checkToken(syntax, "(", 2); syntax= syntax.getNext(); + checkToken(syntax, "0", 3); syntax= syntax.getNext(); + checkToken(syntax, ")", 4); syntax= syntax.getNext(); + assertNull(syntax); + + tu= parseAndCheckBindings(code + "if (T_P {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + try { + syntax= x.getTrailingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + + tu= parseAndCheckBindings(code + "if (1 P_BLOCK }"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + try { + syntax= x.getTrailingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + + tu= parseAndCheckBindings(code + "IF_COND {}}"); + f= getDeclaration(tu, 0); i= getStatement(f, 0); x= i.getConditionExpression(); + try { + syntax= x.getTrailingSyntax(); + fail(); + } catch (ExpansionOverlapsBoundaryException e) {} + } + + private void checkToken(IToken token, String image, int offset) { + assertEquals(image, token.getImage()); + assertEquals(offset, token.getOffset()); + assertEquals(image.length(), token.getLength()); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java index f37ba6b952e..ef05190e6ec 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java @@ -49,6 +49,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTranslationUnit; import org.eclipse.cdt.internal.core.parser.scanner.ILocationCtx; import org.eclipse.cdt.internal.core.parser.scanner.ImageLocationInfo; import org.eclipse.cdt.internal.core.parser.scanner.LocationMap; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; public class LocationMapTests extends BaseTestCase { public class Loc implements IASTFileLocation { @@ -102,7 +103,7 @@ public class LocationMapTests extends BaseTestCase { @Override protected void setUp() throws Exception { super.setUp(); - fLocationMap= new LocationMap(); + fLocationMap= new LocationMap(new LexerOptions()); } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTVisitor.java index e7169378a9f..50b2be9df4d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTVisitor.java @@ -116,6 +116,38 @@ public abstract class ASTVisitor { */ public boolean shouldVisitTemplateParameters = false; + /** + * Creates a visitor that does not visit any kind of node per default. + */ + public ASTVisitor() { + this(false); + } + + /** + * Creates a visitor. + * @param visitNodes whether visitor is setup to visit all nodes per default. + * @since 5.1 + */ + public ASTVisitor(boolean visitNodes) { + shouldVisitArrayModifiers= visitNodes; + shouldVisitBaseSpecifiers= visitNodes; + shouldVisitDeclarations= visitNodes; + shouldVisitDeclarators= visitNodes; + shouldVisitDeclSpecifiers= visitNodes; + shouldVisitDesignators= visitNodes; + shouldVisitEnumerators= visitNodes; + shouldVisitExpressions= visitNodes; + shouldVisitInitializers= visitNodes; + shouldVisitNames= visitNodes; + shouldVisitNamespaces= visitNodes; + shouldVisitParameterDeclarations= visitNodes; + shouldVisitProblems= visitNodes; + shouldVisitStatements= visitNodes; + shouldVisitTemplateParameters= visitNodes; + shouldVisitTranslationUnit= visitNodes; + shouldVisitTypeIds= visitNodes; + } + // visit methods public int visit(IASTTranslationUnit tu) { return PROCESS_CONTINUE; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ExpansionOverlapsBoundaryException.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ExpansionOverlapsBoundaryException.java new file mode 100644 index 00000000000..0a67d582abf --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ExpansionOverlapsBoundaryException.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2008 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.core.dom.ast; + +/** + * See {@link IASTNode#getTrailingSyntax()} and {@link IASTNode#getLeadingSyntax()}. + * @since 5.1 + */ +public class ExpansionOverlapsBoundaryException extends Exception { + +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTNode.java index 0e6d288390d..9bb7216d81c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTNode.java @@ -6,16 +6,18 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * IBM - Initial API and implementation + * Doug Schaefer - Initial API and implementation * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.core.dom.ast; +import org.eclipse.cdt.core.parser.IToken; + /** * This is the root node in the physical AST. A physical node represents a chunk * of text in the source program. * - * @author Doug Schaefer + * @noimplement This interface is not intended to be implemented by clients. */ public interface IASTNode { @@ -128,4 +130,48 @@ public interface IASTNode { * @since 4.0 */ public boolean contains(IASTNode node); + + /** + * Returns the tokens that can be found between this node and its left sibling (or the + * beginning of the parent, if there is no left sibling). The tokens are obtained + * from the lexer, no preprocessing is performed. + * The offsets of the tokens are relative to the file-offset of this node. + *

Examples looking at the condition of if-statements: + *

+	 * #define IF      if
+     * #define IF_P    if (
+     * #define IF_P_T  if (true
+     * #define SEMI_IF ; if 
+     * #define IF_COND if (true)
+     * void test() {
+     *    if (true) {}       // leading syntax: 'if ('
+     *    IF (true) {}       // leading syntax: 'IF ('
+     *    IF_P true) {}      // leading syntax: 'IF_P'
+     *    IF_P_T ) {}        // throws ExpansionOverlapsBoundaryException
+     *    SEMI_IF (true) {}  // throws ExpansionOverlapsBoundaryException
+     *    IF_COND            // throws ExpansionOverlapsBoundaryException
+     * 
+ * @return a chain of tokens or null, if there are none. + * @throws ExpansionOverlapsBoundaryException if one of the boundaries of the leading syntax is + * overlapped by a macro-expansion. + * @throws UnsupportedOperationException if invoked on preprocessor nodes, or nodes that are not + * part of a translation unit. + * @since 5.1 + */ + public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException; + + /** + * Returns the tokens that can be found between this node and its right sibling (or the + * end of the parent, if there is no right sibling). The tokens are obtained from the lexer, + * no preprocessing is performed. + * The offsets of the tokens are relative to the file-offset of the end of this node. + *

For examples see {@link #getLeadingSyntax()}. + * @return a chain of tokens or null, if there are none. + * @throws ExpansionOverlapsBoundaryException if one of the boundaries of the trailing syntax is + * overlapped by a macro-expansion. + * @throws UnsupportedOperationException if invoked on preprocessor nodes, or nodes that are not + * part of a translation unit. + * @since 5.1 + */ + public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNode.java index 5b1e725b34b..538bfe82fa8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNode.java @@ -1,27 +1,34 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 IBM Corporation and others. + * Copyright (c) 2005, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * IBM Rational Software - Initial API and implementation + * John Camelon - Initial API and implementation * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTImageLocation; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.parser.IToken; +import org.eclipse.cdt.core.parser.OffsetLimitReachedException; +import org.eclipse.cdt.internal.core.parser.scanner.ILexerLog; import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer; +import org.eclipse.cdt.internal.core.parser.scanner.Token; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; /** - * @author jcamelon + * Base class for all non-preprocessor nodes in the AST. */ public abstract class ASTNode implements IASTNode { @@ -182,4 +189,98 @@ public abstract class ASTNode implements IASTNode { } return false; } + + public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException { + int left= getBoundary(-1); + return getSyntax(left, offset, -1); + } + + public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException { + int right= getBoundary(1); + return getSyntax(offset+length, right, 1); + } + + /** + * Compute the sequence number of the boundary of the leading/trailing syntax. + */ + private int getBoundary(int direction) { + ASTNodeSearch visitor= new ASTNodeSearch(this); + IASTNode sib= direction < 0 ? visitor.findLeftSibling() : visitor.findRightSibling(); + if (sib == null) { + direction= -direction; + sib= getParent(); + } + if (sib instanceof ASTNode) { + ASTNode astNode= (ASTNode) sib; + int offset= astNode.getOffset(); + if (direction < 0) { + offset+= astNode.getLength(); + } + return offset; + } + // no parent + throw new UnsupportedOperationException(); + } + + + private IToken getSyntax(int fromSequenceNumber, int nextSequenceNumber, int direction) throws ExpansionOverlapsBoundaryException { + final IASTTranslationUnit tu= getTranslationUnit(); + if (!(tu instanceof ASTNode)) + throw new UnsupportedOperationException(); + + ILocationResolver lr= (ILocationResolver) tu.getAdapter(ILocationResolver.class); + if (lr == null) + throw new UnsupportedOperationException(); + + int endSequenceNumber= lr.convertToSequenceEndNumber(nextSequenceNumber); + IASTFileLocation total= lr.getMappedFileLocation(fromSequenceNumber, endSequenceNumber-fromSequenceNumber); + IASTFileLocation myfloc= getFileLocation(); + if (total == null || myfloc == null) + throw new UnsupportedOperationException(); + + if (!total.getFileName().equals(myfloc.getFileName())) + throw new ExpansionOverlapsBoundaryException(); + + if (fromSequenceNumber > 0) { + IASTFileLocation fl= lr.getMappedFileLocation(fromSequenceNumber-1, endSequenceNumber-fromSequenceNumber+1); + if (fl.getFileName().equals(total.getFileName()) && fl.getNodeOffset() == total.getNodeOffset()) + throw new ExpansionOverlapsBoundaryException(); + } + + if (endSequenceNumber < ((ASTNode) tu).getOffset() + ((ASTNode) tu).getLength()) { + IASTFileLocation fl= lr.getMappedFileLocation(fromSequenceNumber, nextSequenceNumber-fromSequenceNumber+1); + if (fl.getFileName().equals(total.getFileName()) && fl.getNodeLength() == total.getNodeLength()) + throw new ExpansionOverlapsBoundaryException(); + } + + int adjustment= total.getNodeOffset() - myfloc.getNodeOffset(); + if (direction > 0) { + adjustment-= myfloc.getNodeLength(); + } + + char[] txt= lr.getUnpreprocessedSignature(total); + Lexer lex= new Lexer(txt, (LexerOptions) tu.getAdapter(LexerOptions.class), ILexerLog.NULL, null); + try { + Token result= lex.nextToken(); + if (result.getType() == IToken.tEND_OF_INPUT) + return null; + + Token last= result; + for(;;) { + int offset= last.getOffset() + adjustment; + int endOffset= last.getEndOffset() + adjustment; + last.setOffset(offset, endOffset); + + Token t= lex.nextToken(); + if (t.getType() == IToken.tEND_OF_INPUT) + break; + last.setNext(t); + last= t; + } + return result; + } catch (OffsetLimitReachedException e) { + // does not happen without using content assist limit + } + return null; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNodeSearch.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNodeSearch.java new file mode 100644 index 00000000000..8886da416cf --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTNodeSearch.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2008 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; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTArrayModifier; +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.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.IASTProblem; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IASTTypeId; +import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; +import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator; +import org.eclipse.cdt.core.dom.ast.c.ICASTVisitor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisitor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; + +/** + * Utility class to search for siblings of an ast node + */ +public class ASTNodeSearch extends ASTVisitor implements ICASTVisitor, ICPPASTVisitor { + private static final int LEFT = 0; + private static final int RIGHT= 1; + private int fMode; + private IASTNode fLeft; + private IASTNode fRight; + private final IASTNode fNode; + private final IASTNode fParent; + + public ASTNodeSearch(IASTNode node) { + super(true); + fNode= node; + fParent= node.getParent(); + } + + public IASTNode findLeftSibling() { + if (fParent == null) + return null; + + fMode= LEFT; + fLeft= fRight= null; + fParent.accept(this); + return fLeft; + } + + public IASTNode findRightSibling() { + if (fParent == null) + return null; + + fMode= RIGHT; + fLeft= fRight= null; + fParent.accept(this); + return fRight; + } + + private int process(IASTNode node) { + if (node == fParent) + return PROCESS_CONTINUE; + + switch(fMode) { + case LEFT: + if (node == fNode) + return PROCESS_ABORT; + fLeft= node; + return PROCESS_SKIP; + case RIGHT: + if (node == fNode) { + fLeft= fNode; + } else if (fLeft != null) { + fRight= node; + return PROCESS_ABORT; + } + return PROCESS_SKIP; + } + return PROCESS_SKIP; + } + + + public int visit(ICASTDesignator designator) { + return process(designator); + } + + @Override + public int visit(IASTArrayModifier arrayModifier) { + return process(arrayModifier); + } + + @Override + public int visit(IASTDeclaration declaration) { + return process(declaration); + } + + @Override + public int visit(IASTDeclarator declarator) { + return process(declarator); + } + + @Override + public int visit(IASTDeclSpecifier declSpec) { + return process(declSpec); + } + + @Override + public int visit(IASTEnumerator enumerator) { + return process(enumerator); + } + + @Override + public int visit(IASTExpression expression) { + return process(expression); + } + + @Override + public int visit(IASTInitializer initializer) { + return process(initializer); + } + + @Override + public int visit(IASTName name) { + return process(name); + } + + @Override + public int visit(IASTParameterDeclaration parameterDeclaration) { + return process(parameterDeclaration); + } + + @Override + public int visit(IASTProblem problem) { + return process(problem); + } + + @Override + public int visit(IASTStatement statement) { + return process(statement); + } + + @Override + public int visit(IASTTranslationUnit tu) { + return process(tu); + } + + @Override + public int visit(IASTTypeId typeId) { + return process(typeId); + } + + public int visit(ICPPASTBaseSpecifier baseSpecifier) { + return process(baseSpecifier); + } + + public int visit(ICPPASTNamespaceDefinition namespaceDefinition) { + return process(namespaceDefinition); + } + + public int visit(ICPPASTTemplateParameter templateParameter) { + return process(templateParameter); + } + + public int leave(ICASTDesignator designator) { + return PROCESS_CONTINUE; + } + public int leave(ICPPASTBaseSpecifier baseSpecifier) { + return PROCESS_CONTINUE; + } + + public int leave(ICPPASTNamespaceDefinition namespaceDefinition) { + return PROCESS_CONTINUE; + } + + public int leave(ICPPASTTemplateParameter templateParameter) { + return PROCESS_CONTINUE; + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java index 53498f3ac58..44ee8087166 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java @@ -37,6 +37,7 @@ import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver; import org.eclipse.cdt.internal.core.parser.scanner.ISkippedIndexedFilesListener; import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.core.runtime.CoreException; /** @@ -294,6 +295,9 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat if (adapter.isAssignableFrom(IIndexFileSet.class)) { return fIndexFileSet; } + if (adapter.isAssignableFrom(LexerOptions.class)) { + return fLocationResolver.getLexerOptions(); + } return null; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java index 753a2515baf..2663056893a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java @@ -16,6 +16,7 @@ import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ASTWriterVisitor; /** @@ -78,4 +79,12 @@ public class ASTLiteralNode implements IASTNode { public void setPropertyInParent(ASTNodeProperty property) { } + + public IToken getLeadingSyntax() { + throw new UnsupportedOperationException(); + } + + public IToken getTrailingSyntax() { + throw new UnsupportedOperationException(); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java index e62f74a19c5..3a3382e75c9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java @@ -41,6 +41,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IMacroBinding; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree.IASTInclusionNode; +import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.ASTNodeSpecification; @@ -77,6 +78,16 @@ abstract class ASTPreprocessorNode extends ASTNode { void findNode(ASTNodeSpecification nodeSpec) { nodeSpec.visit(this); } + + @Override + public IToken getLeadingSyntax() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public IToken getTrailingSyntax() { + throw new UnsupportedOperationException(); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 7ffea6ca118..448b484ed13 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -138,7 +138,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { // state information private final CharArrayMap fMacroDictionary = new CharArrayMap(512); - private final LocationMap fLocationMap = new LocationMap(); + private final LocationMap fLocationMap; /** Set of already included files */ private final HashSet fAllIncludedFiles= new HashSet(); @@ -161,6 +161,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { fLexOptions.fSupportAtSignInIdentifiers= configuration.supportAtSignInIdentifiers(); fLexOptions.fSupportMinAndMax = configuration.supportMinAndMaxOperators(); fLexOptions.fSupportSlashPercentComments= configuration.supportSlashPercentComments(); + fLocationMap= new LocationMap(fLexOptions); fKeywords= new CharArrayIntMap(40, -1); fPPKeywords= new CharArrayIntMap(40, -1); configureKeywords(language, configuration); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ILocationResolver.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ILocationResolver.java index 450d81d2824..5518b4822b6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ILocationResolver.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ILocationResolver.java @@ -26,6 +26,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IMacroBinding; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree; import org.eclipse.cdt.internal.core.dom.parser.ASTNodeSpecification; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; /** @@ -163,4 +164,16 @@ public interface ILocationResolver { * @since 5.0 */ IASTPreprocessorMacroExpansion[] getMacroExpansions(IASTFileLocation loc); + + /** + * If you want to use the sequence number of an ast-node as the end of a previous node, + * it needs to be adjusted, because gaps are used for the encoding of directives. + * @return the adjusted sequence number, to be used as end-number + */ + int convertToSequenceEndNumber(int sequenceNumber); + + /** + * Returns the lexer options that have been used by the preprocessor. + */ + LexerOptions getLexerOptions(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtx.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtx.java index 4eef1b81495..b25e540854e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtx.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtx.java @@ -98,6 +98,14 @@ abstract class LocationCtx implements ILocationCtx { return null; } + public int convertToSequenceEndNumber(int sequenceNumber) { + // if the sequence number is the beginning of this context, skip the denotation of this + // context in the parent. + if (sequenceNumber == fSequenceNumber) + return sequenceNumber - fEndOffsetInParent + fOffsetInParent; + return sequenceNumber; + } + /** * Returns the minimal file location containing the specified sequence number range, assuming * that it is contained in this context. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxContainer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxContainer.java index 91f4ff55ade..b2c3688d68d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxContainer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationCtxContainer.java @@ -109,6 +109,21 @@ class LocationCtxContainer extends LocationCtx { return null; } + @Override + public int convertToSequenceEndNumber(int sequenceNumber) { + // try to delegate to a child. + final LocationCtx child= findChildLessOrEqualThan(sequenceNumber, false); + if (child != null) + sequenceNumber= child.convertToSequenceEndNumber(sequenceNumber); + + // if the potentially converted sequence number is the beginning of this context, + // skip the denotation of this context in the parent. + if (sequenceNumber == fSequenceNumber) + return sequenceNumber - fEndOffsetInParent + fOffsetInParent; + + return sequenceNumber; + } + @Override public ASTFileLocation findMappedFileLocation(int sequenceNumber, int length) { // try to delegate to a child. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java index 350642f8700..ea25fb2d703 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java @@ -34,6 +34,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ASTNodeSpecification; import org.eclipse.cdt.internal.core.dom.parser.ASTProblem; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; /** * Converts the offsets relative to various contexts to the global sequence number. Also creates and stores @@ -43,6 +44,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTProblem; public class LocationMap implements ILocationResolver { private static final IASTName[] EMPTY_NAMES = {}; + private final LexerOptions fLexerOptions; private String fTranslationUnitPath; private IASTTranslationUnit fTranslationUnit; @@ -59,7 +61,15 @@ public class LocationMap implements ILocationResolver { // stuff computed on demand private IdentityHashMap fMacroDefinitionMap= null; private List fSkippedFilesListeners= new ArrayList(); - + + public LocationMap(LexerOptions lexOptions) { + fLexerOptions= lexOptions; + } + + public LexerOptions getLexerOptions() { + return fLexerOptions; + } + public void registerPredefinedMacro(IMacroBinding macro) { registerPredefinedMacro(macro, null, -1); } @@ -392,7 +402,11 @@ public class LocationMap implements ILocationResolver { return fRootContext.findMappedFileLocation(sequenceNumber, length); } - public char[] getUnpreprocessedSignature(IASTFileLocation loc) { + public int convertToSequenceEndNumber(int sequenceNumber) { + return fRootContext.convertToSequenceEndNumber(sequenceNumber); + } + + public char[] getUnpreprocessedSignature(IASTFileLocation loc) { ASTFileLocation floc= convertFileLocation(loc); if (floc == null) { return CharArrayUtils.EMPTY; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MultiMacroExpansionExplorer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MultiMacroExpansionExplorer.java index 2d6a9393d6d..a307d673c4b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MultiMacroExpansionExplorer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MultiMacroExpansionExplorer.java @@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IMacroBinding; import org.eclipse.cdt.core.dom.rewrite.MacroExpansionExplorer; +import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.jface.text.IRegion; import org.eclipse.text.edits.ReplaceEdit; @@ -99,7 +100,8 @@ public class MultiMacroExpansionExplorer extends MacroExpansionExplorer { fBoundaries[++bidx]= to; fDelegates[++didx]= new SingleMacroExpansionExplorer(new String(fSource, from, to-from), refs.toArray(new IASTName[refs.size()]), fMacroLocations, - fFilePath, refLoc.getStartingLineNumber(), isPPCond); + fFilePath, refLoc.getStartingLineNumber(), isPPCond, + (LexerOptions) tu.getAdapter(LexerOptions.class)); } } fBoundaries[++bidx]= fSource.length; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/SingleMacroExpansionExplorer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/SingleMacroExpansionExplorer.java index 492d930fd71..34900655913 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/SingleMacroExpansionExplorer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/SingleMacroExpansionExplorer.java @@ -26,12 +26,6 @@ import org.eclipse.text.edits.ReplaceEdit; * @since 5.0 */ public class SingleMacroExpansionExplorer extends MacroExpansionExplorer { - - private static final LexerOptions LEX_OPTIONS= new LexerOptions(); - static { - LEX_OPTIONS.fCreateImageLocations= false; - } - private final String fInput; private final CharArrayMap fDictionary; private MacroExpansionStep fFullExpansion; @@ -40,16 +34,19 @@ public class SingleMacroExpansionExplorer extends MacroExpansionExplorer { private final int fLineNumber; private final Map fMacroLocationMap; private final boolean fIsPPCondition; + private final LexerOptions fLexerOptions; public SingleMacroExpansionExplorer(String input, IASTName[] refs, Map macroDefinitionLocationMap, - String filePath, int lineNumber, boolean isPPCondition) { + String filePath, int lineNumber, boolean isPPCondition, LexerOptions options) { fInput= input; fDictionary= createDictionary(refs); fMacroLocationMap= macroDefinitionLocationMap; fFilePath= filePath; fLineNumber= lineNumber; fIsPPCondition= isPPCondition; + fLexerOptions= (LexerOptions) options.clone(); + fLexerOptions.fCreateImageLocations= false; } private CharArrayMap createDictionary(IASTName[] refs) { @@ -80,7 +77,7 @@ public class SingleMacroExpansionExplorer extends MacroExpansionExplorer { } private void computeExpansion() { - MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, LEX_OPTIONS); + MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, fLexerOptions); MacroExpansionTracker tracker= new MacroExpansionTracker(Integer.MAX_VALUE); expander.expand(fInput, tracker, fFilePath, fLineNumber, fIsPPCondition); @@ -96,7 +93,7 @@ public class SingleMacroExpansionExplorer extends MacroExpansionExplorer { if (step < 0 || step >= fExpansionCount) { throw new IndexOutOfBoundsException(); } - MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, LEX_OPTIONS); + MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, fLexerOptions); MacroExpansionTracker tracker= new MacroExpansionTracker(step); expander.expand(fInput, tracker, fFilePath, fLineNumber, fIsPPCondition); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMASTAdapter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMASTAdapter.java index 12410f7b182..4561fcd5d61 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMASTAdapter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMASTAdapter.java @@ -15,6 +15,7 @@ import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.IASTCompletionContext; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTImageLocation; @@ -36,6 +37,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.index.IIndexBinding; +import org.eclipse.cdt.core.parser.IToken; import org.eclipse.core.runtime.CoreException; public class PDOMASTAdapter { @@ -171,6 +173,16 @@ public class PDOMASTAdapter { public IASTName getLastName() { return this; } + + public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException, + UnsupportedOperationException { + return fDelegate.getLeadingSyntax(); + } + + public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException, + UnsupportedOperationException { + return fDelegate.getTrailingSyntax(); + } } private static class AnonymousEnumeration implements IEnumeration {