1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-31 04:45:38 +02:00

Bug 533552: Rewriting nodes with attributes causes attribute duplication

Fix and tests.

Also fixes bug 535265, since the Codan Quickfix tests fail otherwise.

Change-Id: Id31e40907b7ebdeee4a67c014c3a1b1cd37579ad
Signed-off-by: Hansruedi Patzen <hansruedi.patzen@hsr.ch>
Signed-off-by: Thomas Corbat <tcorbat@hsr.ch>
This commit is contained in:
Hansruedi Patzen 2018-06-01 10:59:07 +02:00 committed by Thomas Corbat
parent 75e8ed9fc5
commit 2ca147ebf1
9 changed files with 474 additions and 29 deletions

View file

@ -40,7 +40,8 @@ Require-Bundle: org.eclipse.core.resources,
org.eclipse.ui,
org.eclipse.jface.text,
org.eclipse.core.filesystem,
org.eclipse.ltk.core.refactoring;bundle-version="3.4.0"
org.eclipse.ltk.core.refactoring;bundle-version="3.4.0",
org.hamcrest
Bundle-ActivationPolicy: lazy
Bundle-Vendor: Eclipse CDT
Bundle-RequiredExecutionEnvironment: JavaSE-1.8

View file

@ -11,6 +11,9 @@
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.rewrite.changegenerator;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import org.eclipse.cdt.core.CCorePlugin;
@ -71,9 +74,24 @@ public abstract class ChangeGeneratorTest extends BaseTestFramework {
}
protected void compareResult(ASTVisitor visitor) throws Exception {
compareResult(visitor, false);
}
protected void compareResult(ASTVisitor visitor, boolean shouldValidateAST) throws Exception {
final StringBuilder[] testSources = getTestSource(2);
final String source = testSources[0].toString();
final String expected = testSources[1].toString();
compareResult(visitor, testSources[0].toString(), testSources[1].toString(), shouldValidateAST);
}
protected void compareCopyResult(ASTVisitor visitor) throws Exception {
compareCopyResult(visitor, true);
}
protected void compareCopyResult(ASTVisitor visitor, boolean shouldValidateAST) throws Exception {
final StringBuilder[] testSources = getTestSource(1);
compareResult(visitor, testSources[0].toString(), testSources[0].toString(), shouldValidateAST);
}
private void compareResult(ASTVisitor visitor, String source, String expected, boolean shouldValidateAST) throws Exception {
final IFile testFile = importFile("source.h", source); //$NON-NLS-1$
CCorePlugin.getIndexManager().reindex(cproject);
@ -83,13 +101,22 @@ public abstract class ChangeGeneratorTest extends BaseTestFramework {
waitForIndexer(cproject);
final IASTTranslationUnit unit = CoreModelUtil.findTranslationUnit(testFile).getAST();
if (shouldValidateAST) {
ProblemNodeChecker validator = new ProblemNodeChecker();
unit.accept(validator);
assertFalse("Problem nodes found, AST is invalid.", validator.problemsFound());
}
final ChangeGenerator changeGenerator =
new ChangeGenerator(modStore, ASTCommenter.getCommentedNodeMap(unit));
unit.accept(visitor);
changeGenerator.generateChange(unit);
final Document doc = new Document(source);
for (Change change : ((CompositeChange) changeGenerator.getChange()).getChildren()) {
Change[] changes = ((CompositeChange) changeGenerator.getChange()).getChildren();
assertThat("No changes found",changes.length, is(greaterThan(0)));
for (Change change : changes) {
if (change instanceof TextFileChange) {
TextFileChange textChange = (TextFileChange) change;
textChange.getEdit().apply(doc);

View file

@ -0,0 +1,195 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
* 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:
* Hansruedi Patzen (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.rewrite.changegenerator;
import java.util.function.Predicate;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTAttribute;
import org.eclipse.cdt.core.dom.ast.IASTAttributeSpecifier;
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.IASTEnumerationSpecifier.IASTEnumerator;
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.IASTNode.CopyStyle;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTToken;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCapture;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTClassVirtSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDecltypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDesignator;
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.ICPPASTVirtSpecifier;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
public class CopyReplaceVisitor extends ASTVisitor {
private ChangeGeneratorTest changeGenereatorTest;
private Predicate<IASTNode> predicate;
public CopyReplaceVisitor(ChangeGeneratorTest changeGenereatorTest, Predicate<IASTNode> predicate) {
super(true);
this.changeGenereatorTest = changeGenereatorTest;
this.predicate = predicate;
}
private int copyReplace(IASTNode node) {
if (predicate.test(node)) {
changeGenereatorTest.addModification(null, ModificationKind.REPLACE, node, node.copy(CopyStyle.withLocations));
return PROCESS_ABORT;
}
return PROCESS_CONTINUE;
}
@Override
public int visit(IASTTranslationUnit tu) {
return copyReplace(tu);
}
@Override
public int visit(IASTName name) {
return copyReplace(name);
}
@Override
public int visit(IASTDeclaration declaration) {
return copyReplace(declaration);
}
@Override
public int visit(IASTInitializer initializer) {
return copyReplace(initializer);
}
@Override
public int visit(IASTParameterDeclaration parameterDeclaration) {
return copyReplace(parameterDeclaration);
}
@Override
public int visit(IASTDeclarator declarator) {
return copyReplace(declarator);
}
@Override
public int visit(IASTDeclSpecifier declSpec) {
return copyReplace(declSpec);
}
@Override
public int visit(IASTArrayModifier arrayModifier) {
return copyReplace(arrayModifier);
}
@Override
public int visit(IASTPointerOperator ptrOperator) {
return copyReplace(ptrOperator);
}
@Override
public int visit(IASTAttribute attribute) {
return copyReplace(attribute);
}
@Override
public int visit(IASTAttributeSpecifier specifier) {
return copyReplace(specifier);
}
@Override
public int visit(IASTToken token) {
return copyReplace(token);
}
@Override
public int visit(IASTExpression expression) {
return copyReplace(expression);
}
@Override
public int visit(IASTStatement statement) {
return copyReplace(statement);
}
@Override
public int visit(IASTTypeId typeId) {
return copyReplace(typeId);
}
@Override
public int visit(IASTEnumerator enumerator) {
return copyReplace(enumerator);
}
@Override
public int visit(IASTProblem problem) {
return copyReplace(problem);
}
@Override
public int visit(ICPPASTBaseSpecifier baseSpecifier) {
return copyReplace(baseSpecifier);
}
@Override
public int visit(ICPPASTNamespaceDefinition namespaceDefinition) {
return copyReplace(namespaceDefinition);
}
@Override
public int visit(ICPPASTTemplateParameter templateParameter) {
return copyReplace(templateParameter);
}
@Override
public int visit(ICPPASTCapture capture) {
return copyReplace(capture);
}
@Override
public int visit(ICASTDesignator designator) {
return copyReplace(designator);
}
@Override
public int visit(ICPPASTDesignator designator) {
return copyReplace(designator);
}
@Override
public int visit(ICPPASTVirtSpecifier virtSpecifier) {
return copyReplace(virtSpecifier);
}
@Override
public int visit(ICPPASTClassVirtSpecifier classVirtSpecifier) {
return copyReplace(classVirtSpecifier);
}
@Override
public int visit(ICPPASTDecltypeSpecifier decltypeSpecifier) {
return copyReplace(decltypeSpecifier);
}
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
* 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:
* Institute for Software - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.rewrite.changegenerator;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
public class ProblemNodeChecker extends ASTVisitor {
private boolean problemFound = false;
{
shouldVisitProblems = true;
}
@Override
public int visit(IASTProblem problem) {
problemFound = true;
return PROCESS_ABORT;
}
public boolean problemsFound() {
return problemFound;
}
}

View file

@ -14,11 +14,13 @@ package org.eclipse.cdt.core.parser.tests.rewrite.changegenerator;
import static org.eclipse.cdt.core.dom.ast.IASTLiteralExpression.lk_integer_constant;
import static org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind.REPLACE;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTAttribute;
import org.eclipse.cdt.core.dom.ast.IASTAttributeList;
import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
@ -41,9 +43,13 @@ import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointer;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAttributeList;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
@ -65,12 +71,50 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTUnaryExpression;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
import junit.framework.TestSuite;
public class ReplaceTests extends ChangeGeneratorTest {
public static TestSuite suite() {
return new TestSuite(ReplaceTests.class);
}
private IASTAttribute createAttribute(String name) {
return factory.newAttribute(name.toCharArray(), null);
}
private IASTAttributeOwner copy(IASTAttributeOwner owner) {
return (IASTAttributeOwner) owner.copy(CopyStyle.withLocations);
}
/**
* Adds an Attribute to an existing IASTAttributeList
*
* @param owner IASTAttributeOwner
* @param attributeName Name of the new Attribute
* @param index Index of existing IASTAttributeList
*/
private void addAttributeToListModification(IASTAttributeOwner owner, String attributeName, int index) {
IASTAttributeOwner copy = copy(owner);
IASTAttributeList attributeList = (IASTAttributeList) copy.getAttributeSpecifiers()[index];
attributeList.addAttribute(createAttribute(attributeName));
addModification(null, ModificationKind.REPLACE, owner, copy);
}
/**
* Addds a new AttributeList to a IASTAttributeOwner
*
* @param owner IASTAttributeOwner
* @param attributeName Name of the new Attribute
*/
private void addAttributeListModification(IASTAttributeOwner owner, String attributeName) {
IASTAttributeOwner copy = copy(owner);
ICPPASTAttributeList attributeList = factory.newAttributeList();
attributeList.addAttribute(createAttribute(attributeName));
copy.addAttributeSpecifier(attributeList);
addModification(null, ModificationKind.REPLACE, owner, copy);
}
//int *pi[3];
//int *pi[15];
@ -1045,4 +1089,81 @@ public class ReplaceTests extends ChangeGeneratorTest {
}
});
}
//[[foo]] int hs = 5;
public void testCopyReplaceAttribute_Bug533552_1a() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, IASTDeclaration.class::isInstance));
}
//[[foo, bar]][[foobar]] int hs = 5;
public void testCopyReplaceAttribute_Bug533552_1b() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, IASTDeclaration.class::isInstance));
}
//[[foo, bar]][[foobar]] int [[asdf]] hs = 5;
public void testCopyReplaceAttribute_Bug533552_1c() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, IASTDeclaration.class::isInstance));
}
//using I [[attribute]] = int;
public void testCopyReplaceAliasDeclarationWithAttributes_Bug533552_1d() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, ICPPASTAliasDeclaration.class::isInstance));
}
//int i [[attribute]];
public void testCopyReplaceDeclaratorWithAttributes_Bug533552_1e() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, IASTDeclarator.class::isInstance));
}
//[[foo]] int hs = 5;
//[[foo, bar]] int hs = 5;
public void testAddAttribute_Bug533552_2a() throws Exception {
compareResult(new ASTVisitor() {
{
shouldVisitDeclarations = true;
}
@Override
public int visit(IASTDeclaration declaration) {
if (declaration instanceof IASTSimpleDeclaration) {
addAttributeToListModification((IASTSimpleDeclaration) declaration, "bar", 0);
return PROCESS_ABORT;
}
return PROCESS_CONTINUE;
}
});
}
//[[foo]] int hs = 5;
//[[foo]][[bar]] int hs = 5;
public void testAddAttribute_Bug533552_2b() throws Exception {
compareResult(new ASTVisitor() {
{
shouldVisitDeclarations = true;
}
@Override
public int visit(IASTDeclaration declaration) {
if (declaration instanceof IASTSimpleDeclaration) {
addAttributeListModification((IASTSimpleDeclaration) declaration, "bar");
return PROCESS_ABORT;
}
return PROCESS_CONTINUE;
}
});
}
//void f() {
// switch (1) {
// case 1:
// [[fallthrough]];
// case 2:
// break;
// }
//}
public void testCopyReplaceAttribute_Bug535265_1() throws Exception {
compareCopyResult(new CopyReplaceVisitor(this, IASTSwitchStatement.class::isInstance));
}
}

View file

@ -1573,10 +1573,11 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
final IASTName etorName= identifier();
final IASTEnumerator enumerator= nodeFactory.newEnumerator(etorName, null);
endOffset= calculateEndOffset(etorName);
setRange(enumerator, problemOffset, endOffset);
List<IASTAttributeSpecifier> attributes = __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
addAttributeSpecifiers(attributes, enumerator);
List<IASTAttributeSpecifier> attributes = __attribute_decl_seq(supportAttributeSpecifiers, supportDeclspecSpecifiers);
addAttributeSpecifiers(attributes, enumerator);
endOffset = attributesEndOffset(endOffset, attributes);
setRange(enumerator, problemOffset, endOffset);
result.addEnumerator(enumerator);
if (LTcatchEOF(1) == IToken.tASSIGN) {
@ -2434,7 +2435,8 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
return null;
IASTAttributeList result = nodeFactory.newGCCAttributeList();
consume();
final int startOffset = consume().getOffset();
int endOffset = startOffset;
if (LT(1) == IToken.tLPAREN) {
consume();
consume(IToken.tLPAREN);
@ -2456,8 +2458,9 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
}
consumeOrEOC(IToken.tRPAREN);
consumeOrEOC(IToken.tRPAREN);
endOffset = consumeOrEOC(IToken.tRPAREN).getEndOffset();
}
setRange(result, startOffset, endOffset);
return result;
}
@ -2480,8 +2483,25 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
return result;
}
protected void addAttributeSpecifiers(List<IASTAttributeSpecifier> specifiers, IASTAttributeOwner owner) {
if (specifiers != null && owner != null) {
protected final int attributesStartOffset(int startOffset, List<IASTAttributeSpecifier> specifiers) {
if (specifiers == null || specifiers.isEmpty()) {
return startOffset;
}
ASTNode firstSpecifier = (ASTNode) specifiers.get(0);
return Math.min(startOffset, firstSpecifier.getOffset());
}
protected final int attributesEndOffset(int endOffset, List<IASTAttributeSpecifier> specifiers) {
if (specifiers == null || specifiers.isEmpty()) {
return endOffset;
}
ASTNode lastSpecifier = (ASTNode) specifiers.get(specifiers.size() - 1);
return Math.max(endOffset, calculateEndOffset(lastSpecifier));
}
protected final void addAttributeSpecifiers(List<IASTAttributeSpecifier> specifiers, IASTAttributeOwner owner) {
assert owner != null;
if (specifiers != null) {
for (IASTAttributeSpecifier specifier : specifiers) {
owner.addAttributeSpecifier(specifier);
}

View file

@ -1213,7 +1213,9 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser {
result= buildSimpleDeclSpec(storageClass, simpleType, options, isLong, typeofExpression, offset, endOffset);
}
result.setAlignmentSpecifiers(ArrayUtil.trim(alignmentSpecifiers));
addAttributeSpecifiers(attributes, result);
addAttributeSpecifiers(attributes, result);
endOffset = attributesEndOffset(endOffset, attributes);
setRange(result, offset, endOffset);
} catch (BacktrackException e) {
if (returnToken != null) {
backup(returnToken);

View file

@ -2241,8 +2241,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
addAttributeSpecifiers(attributes, astUD);
((ASTNode) astUD).setOffsetAndLength(offset, endOffset - offset);
return astUD;
return setRange(astUD, offset, endOffset);
}
if (LT(1) == IToken.tIDENTIFIER
@ -2278,8 +2277,8 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
ICPPASTAliasDeclaration aliasDeclaration = getNodeFactory().newAliasDeclaration(aliasName, aliasedType);
addAttributeSpecifiers(attributes, aliasDeclaration);
setRange(aliasDeclaration, offset, endOffset);
return aliasDeclaration;
return setRange(aliasDeclaration, offset, endOffset);
}
private ICPPASTUsingDeclaration usingDeclaration(final int offset) throws EndOfFileException, BacktrackException {
@ -2840,7 +2839,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
if (LT(1) == IToken.tLBRACE)
throwBacktrack(LA(1));
final int firstOffset= LA(1).getOffset();
final int firstOffset= attributesStartOffset(LA(1).getOffset(), attributes);
int endOffset= firstOffset;
boolean insertSemi= false;
@ -3553,6 +3552,8 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
result= buildSimpleDeclSpec(storageClass, simpleType, options, isLong, typeofExpression, offset, endOffset);
}
addAttributeSpecifiers(attributes, result);
attributesEndOffset(endOffset, attributes);
setRange(result, offset, endOffset);
} catch (BacktrackException e) {
if (returnToken != null) {
backup(returnToken);
@ -4510,13 +4511,14 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
pointer.setConst(isConst);
pointer.setVolatile(isVolatile);
pointer.setRestrict(isRestrict);
setRange(pointer, startOffset, endOffset);
if (result == null) {
result= new ArrayList<>(4);
}
attributes = CollectionUtils.merge(attributes, attributeSpecifierSeq());
addAttributeSpecifiers(attributes, pointer);
endOffset = attributesEndOffset(endOffset, attributes);
setRange(pointer, startOffset, endOffset);
result.add(pointer);
}
@ -4595,8 +4597,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
}
addAttributeSpecifiers(attributes, result);
endOffset = attributesEndOffset(endOffset, attributes);
setRange(result, startingOffset, endOffset);
((ASTNode) result).setOffsetAndLength(startingOffset, endOffset - startingOffset);
return result;
}
@ -4757,9 +4760,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
attributes = CollectionUtils.merge(attributes, attributeSpecifierSeq());
addAttributeSpecifiers(attributes, fc);
if (attributes != null && !attributes.isEmpty()) {
endOffset = getEndOffset();
}
endOffset = attributesEndOffset(endOffset, attributes);
if (LT(1) == IToken.tARROW) {
consume();
@ -5097,27 +5098,29 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
throws EndOfFileException, BacktrackException {
boolean allowExpression= option == DeclarationOptions.TYPEID_NEW;
while (LT(1) == IToken.tLBRACKET) {
int o = consume().getOffset(); // eat the '['
int startOffset = consume().getOffset(); // eat the '['
IASTExpression exp = null;
if (LT(1) != IToken.tRBRACKET && LT(1) != IToken.tEOC) {
exp = allowExpression ? expression() : constantExpression();
allowExpression= false;
}
int l;
int endOffset;
switch (LT(1)) {
case IToken.tRBRACKET:
case IToken.tEOC:
l = consume().getEndOffset();
endOffset = consume().getEndOffset();
break;
default:
throw backtrack;
}
IASTArrayModifier arrayMod = getNodeFactory().newArrayModifier(exp);
((ASTNode) arrayMod).setOffsetAndLength(o, l - o);
List<IASTAttributeSpecifier> attributes = attributeSpecifierSeq();
addAttributeSpecifiers(attributes, arrayMod);
endOffset = attributesEndOffset(endOffset, attributes);
setRange(arrayMod, startOffset, endOffset);
collection.add(arrayMod);
}
@ -5131,6 +5134,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
@Override
protected IASTStatement statement() throws EndOfFileException, BacktrackException {
int startOffset = LA(1).getOffset();
List<IASTAttributeSpecifier> attributes = attributeSpecifierSeq();
IASTStatement statement = null;
@ -5193,7 +5197,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
return parseDeclarationOrExpressionStatement(attributes);
}
addAttributeSpecifiers(attributes, statement);
return statement;
int endOffset = calculateEndOffset(statement);
return setRange(statement, startOffset, endOffset);
}
protected IASTStatement parseTryStatement() throws EndOfFileException, BacktrackException {

View file

@ -26,6 +26,8 @@ import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression;
import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
import org.eclipse.cdt.core.dom.ast.IASTAttributeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
@ -1532,6 +1534,40 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
return -1;
}
/**
* Formats the attributes leading a node.
* Same as {@code formatAttributes(owner, false, true);}
* @param owner Node containing attributes
*/
private void formatLeadingAttributes(IASTAttributeOwner owner) {
formatAttributes(owner, false, true);
}
/**
* Formats the attributes of a given attribute owner.
*
* @param owner Node containing attributes
* @param printLeadingSpace Print a space before the first attribute
* @param printTrailingSpace Print a space after the last attribute
*/
private void formatAttributes(IASTAttributeOwner owner, boolean printLeadingSpace, boolean printTrailingSpace) {
if (owner == null) {
return;
}
IASTAttributeSpecifier[] attributeSpecifiers = owner.getAttributeSpecifiers();
if (attributeSpecifiers.length > 0) {
if (printLeadingSpace) {
scribe.space();
}
for (IASTAttributeSpecifier attributeSpecifier : attributeSpecifiers) {
formatRaw(attributeSpecifier);
}
if (printTrailingSpace) {
scribe.space();
}
}
}
private void formatPointers(IASTPointerOperator[] pointers) {
for (IASTPointerOperator pointer : pointers) {
if (scribe.printComment()) {
@ -1634,6 +1670,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private int visit(IASTSimpleDeclaration node) {
formatLeadingAttributes(node);
IASTDeclSpecifier declSpec= node.getDeclSpecifier();
declSpec.accept(this);
final List<IASTDeclarator> declarators= Arrays.asList(node.getDeclarators());
@ -3142,6 +3179,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
private int visit(IASTNullStatement node) {
if (!fHasClauseInitStatement && nodeOffset(node) == getCurrentPosition()) {
formatAttributes(node, false, false);
scribe.printNextToken(Token.tSEMI, preferences.insert_space_before_semicolon);
scribe.printTrailingComment();
}