summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHansruedi Patzen2018-06-01 04:59:07 -0400
committerThomas Corbat2018-06-14 04:47:20 -0400
commit2ca147ebf134be986a21b724291a33cbfba9d5ee (patch)
treec35abb1dbdd1ecceb62150510da3d40f74a8dc03
parent75e8ed9fc57253315fb639bada20f596fc559850 (diff)
downloadorg.eclipse.cdt-2ca147ebf134be986a21b724291a33cbfba9d5ee.zip
org.eclipse.cdt-2ca147ebf134be986a21b724291a33cbfba9d5ee.tar.gz
org.eclipse.cdt-2ca147ebf134be986a21b724291a33cbfba9d5ee.tar.xz
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>
-rw-r--r--core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF3
-rw-r--r--core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ChangeGeneratorTest.java33
-rw-r--r--core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/CopyReplaceVisitor.java195
-rw-r--r--core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ProblemNodeChecker.java35
-rw-r--r--core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ReplaceTests.java123
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java36
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java4
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java36
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java38
9 files changed, 474 insertions, 29 deletions
diff --git a/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF
index 4440d94..21c06c6 100644
--- a/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF
+++ b/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF
@@ -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
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ChangeGeneratorTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ChangeGeneratorTest.java
index b47cde4..27bb0a6 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ChangeGeneratorTest.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ChangeGeneratorTest.java
@@ -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);
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/CopyReplaceVisitor.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/CopyReplaceVisitor.java
new file mode 100644
index 0000000..476ab09
--- /dev/null
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/CopyReplaceVisitor.java
@@ -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);
+ }
+} \ No newline at end of file
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ProblemNodeChecker.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ProblemNodeChecker.java
new file mode 100644
index 0000000..b5d4061
--- /dev/null
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ProblemNodeChecker.java
@@ -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;
+ }
+}
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ReplaceTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ReplaceTests.java
index 0a47747..c92f2e6 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ReplaceTests.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/changegenerator/ReplaceTests.java
@@ -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));
+ }
}
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 dce940d..3aa419d 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
@@ -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);
}
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 7d5aeac..42a77d8 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
@@ -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);
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 b040479..4337d5f 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
@@ -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 {
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
index 7831a61..3bb46f3 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
@@ -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();
}