1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 08:55:25 +02:00

Bug 522200: [C++17] Add support for structured binding declarations

Adds support for structured bindings:
- Parser updated
- Semantics updated
- Tests for parser and bindings added

Change-Id: I1de7b760041ac4ce4601f1b5032fdb0a197212a1
Signed-off-by: Hansruedi Patzen <hansruedi.patzen@hsr.ch>
Signed-off-by: Thomas Corbat <tcorbat@hsr.ch>
This commit is contained in:
Thomas Corbat 2018-05-23 13:02:03 +02:00
parent af88842969
commit acbceb04ee
58 changed files with 3043 additions and 163 deletions

View file

@ -335,6 +335,12 @@ public class AST2CPPAttributeTests extends AST2TestBase {
checkAttributeRelations(getAttributeSpecifiers(tu), ICPPASTSimpleDeclSpecifier.class);
}
//auto [[maybe_unused]] variable;
public void testAttributeAutoDeclSpecifer() throws Exception {
IASTTranslationUnit tu = parseAndCheckBindings();
checkAttributeRelations(getAttributeSpecifiers(tu), ICPPASTSimpleDeclSpecifier.class);
}
// const volatile unsigned long int [[attr]] cvuli;
public void testAttributedTypeSpecifier() throws Exception {
IASTTranslationUnit tu = parseAndCheckBindings();

View file

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2017 Nathan Ridge.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPQualifierType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType;
/**
*
* Helper class for common type wrapping operations for tests.
*
*/
public class CommonCPPTypes {
public static IType char_ = CPPBasicType.CHAR;
public static IType int_ = CPPBasicType.INT;
public static IType void_ = CPPBasicType.VOID;
public static IType double_ = new CPPBasicType(Kind.eDouble, 0);
public static IType float_ = new CPPBasicType(Kind.eFloat, 0);
public static IType constChar = constOf(char_);
public static IType constInt = constOf(int_);
public static IType pointerToInt = pointerTo(int_);
public static IType constPointerToInt = constPointerTo(int_);
public static IType pointerToConstChar = pointerTo(constChar);
public static IType pointerToConstInt = pointerTo(constInt);
public static IType referenceToInt = referenceTo(int_);
public static IType referenceToConstInt = referenceTo(constInt);
public static IType rvalueReferenceToInt = rvalueReferenceTo(int_);
public static IType rvalueReferenceToConstInt = rvalueReferenceTo(constInt);
public static IType pointerTo(IType type) {
return new CPPPointerType(type);
}
// Not quite the same as constOf(pointerTo(type)) because of the
// idiosyncratic way we represent cosnt pointers using a flag
// on the CPPPointerType rather than using CPPQualifierType.
private static IType constPointerTo(IType type) {
return new CPPPointerType(type, true, false, false);
}
public static IType constOf(IType type) {
return new CPPQualifierType(type, true, false);
}
public static IType volatileOf(IType type) {
return new CPPQualifierType(type, false, true);
}
public static IType constVolatileOf(IType type) {
return new CPPQualifierType(type, true, true);
}
public static IType referenceTo(IType type) {
return new CPPReferenceType(type, false);
}
public static IType rvalueReferenceTo(IType type) {
return new CPPReferenceType(type, true);
}
}

View file

@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2017 Nathan Ridge.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.c.CBasicType;
import org.eclipse.cdt.internal.core.dom.parser.c.CPointerType;
import org.eclipse.cdt.internal.core.dom.parser.c.CQualifierType;
public class CommonCTypes {
public static IType pointerToVoid = pointerTo(CBasicType.VOID);
public static IType pointerToConstVoid = pointerTo(constOf(CBasicType.VOID));
public static IType pointerToInt = pointerTo(CBasicType.INT);
public static IType pointerToConstInt = pointerTo(constOf(CBasicType.INT));
public static IType pointerToVolatileInt = pointerTo(volatileOf(CBasicType.INT));
public static IType pointerToConstVolatileInt = pointerTo(constVolatileOf(CBasicType.INT));
private static IType pointerTo(IType type) {
return new CPointerType(type, 0);
}
private static IType constOf(IType type) {
return new CQualifierType(type, true, false, false);
}
private static IType volatileOf(IType type) {
return new CQualifierType(type, false, true, false);
}
private static IType constVolatileOf(IType type) {
return new CQualifierType(type, true, true, false);
}
}

View file

@ -21,6 +21,7 @@ import org.eclipse.cdt.core.parser.tests.ast2.cxx14.InitCaptureTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx14.ReturnTypeDeductionTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx14.VariableTemplateTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx17.LambdaExpressionTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx17.StructuredBindingTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx17.TemplateAutoTests;
import org.eclipse.cdt.core.parser.tests.prefix.CompletionTestSuite;
@ -74,6 +75,7 @@ public class DOMParserTestSuite extends TestCase {
// C++17 tests
suite.addTest(TemplateAutoTests.suite());
suite.addTestSuite(LambdaExpressionTests.class);
suite.addTestSuite(StructuredBindingTests.class);
return suite;
}
}

View file

@ -30,13 +30,6 @@ import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
import org.eclipse.cdt.internal.core.dom.parser.c.CBasicType;
import org.eclipse.cdt.internal.core.dom.parser.c.CPointerType;
import org.eclipse.cdt.internal.core.dom.parser.c.CQualifierType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPQualifierType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
/**
@ -51,70 +44,6 @@ public class SemanticTestBase extends BaseTestCase {
super(name);
}
protected static class CommonCTypes {
public static IType pointerToVoid = pointerTo(CBasicType.VOID);
public static IType pointerToConstVoid = pointerTo(constOf(CBasicType.VOID));
public static IType pointerToInt = pointerTo(CBasicType.INT);
public static IType pointerToConstInt = pointerTo(constOf(CBasicType.INT));
public static IType pointerToVolatileInt = pointerTo(volatileOf(CBasicType.INT));
public static IType pointerToConstVolatileInt = pointerTo(constVolatileOf(CBasicType.INT));
private static IType pointerTo(IType type) {
return new CPointerType(type, 0);
}
private static IType constOf(IType type) {
return new CQualifierType(type, true, false, false);
}
private static IType volatileOf(IType type) {
return new CQualifierType(type, false, true, false);
}
private static IType constVolatileOf(IType type) {
return new CQualifierType(type, true, true, false);
}
}
protected static class CommonCPPTypes {
public static IType char_ = CPPBasicType.CHAR;
public static IType int_ = CPPBasicType.INT;
public static IType void_ = CPPBasicType.VOID;
public static IType constChar = constOf(char_);
public static IType constInt = constOf(int_);
public static IType pointerToInt = pointerTo(int_);
public static IType constPointerToInt = constPointerTo(int_);
public static IType pointerToConstChar = pointerTo(constChar);
public static IType pointerToConstInt = pointerTo(constInt);
public static IType referenceToInt = referenceTo(int_);
public static IType referenceToConstInt = referenceTo(constInt);
public static IType rvalueReferenceToInt = rvalueReferenceTo(int_);
public static IType rvalueReferenceToConstInt = rvalueReferenceTo(constInt);
// Not quite the same as constOf(pointerTo(type)) because of the
// idiosyncratic way we represent cosnt pointers using a flag
// on the CPPPointerType rather than using CPPQualifierType.
private static IType constPointerTo(IType type) {
return new CPPPointerType(type, true, false, false);
}
private static IType pointerTo(IType type) {
return new CPPPointerType(type);
}
public static IType constOf(IType type) {
return new CPPQualifierType(type, true, false);
}
private static IType referenceTo(IType type) {
return new CPPReferenceType(type, false);
}
private static IType rvalueReferenceTo(IType type) {
return new CPPReferenceType(type, true);
}
}
protected static void assertSameType(IType expected, IType actual) {
assertNotNull(expected);
assertNotNull(actual);
@ -122,6 +51,10 @@ public class SemanticTestBase extends BaseTestCase {
+ ASTTypeUtil.getType(actual, false) + "'", expected.isSameType(actual));
}
protected static void assertType(IVariable variable, IType expectedType) {
assertSameType(expectedType, variable.getType());
}
protected static SizeAndAlignment getSizeAndAlignment(IType type, IASTNode lookupPoint) {
try {
CPPSemantics.pushLookupPoint(lookupPoint);

View file

@ -11,6 +11,7 @@
package org.eclipse.cdt.core.parser.tests.ast2.cxx14;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
import org.eclipse.cdt.internal.index.tests.IndexBindingResolutionTestBase;
/**

View file

@ -12,6 +12,7 @@ package org.eclipse.cdt.core.parser.tests.ast2.cxx14;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
/**
* AST tests for C++14 generic lambdas.

View file

@ -15,6 +15,7 @@
package org.eclipse.cdt.core.parser.tests.ast2.cxx14;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
/**
* AST tests for C++14 lambda init captures.

View file

@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx14;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
import org.eclipse.cdt.internal.index.tests.IndexBindingResolutionTestBase;
public class ReturnTypeDeductionIndexTests extends IndexBindingResolutionTestBase {

View file

@ -18,6 +18,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType;
public class ReturnTypeDeductionTests extends AST2CPPTestBase {

View file

@ -26,6 +26,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariableInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariableTemplate;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassInstance;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFieldTemplateSpecialization;

View file

@ -65,6 +65,8 @@ public class AllConstexprEvalTests {
suite.addTest(FloatingPointValueTests.SingleProject.suite());
suite.addTest(CStringValueTests.NonIndexing.suite());
suite.addTest(CStringValueTests.SingleProject.suite());
suite.addTest(StructuredBindingTests.NonIndexing.suite());
suite.addTest(StructuredBindingTests.SingleProject.suite());
return suite;
}
}

View file

@ -0,0 +1,468 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences and others
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx14.constexpr;
import junit.framework.TestSuite;
public class StructuredBindingTests extends TestBase {
public static class NonIndexing extends StructuredBindingTests {
public NonIndexing() {
setStrategy(new NonIndexingTestStrategy());
}
public static TestSuite suite() {
return suite(NonIndexing.class);
}
}
public static class SingleProject extends StructuredBindingTests {
public SingleProject() {
setStrategy(new SinglePDOMTestStrategy(true, false));
}
public static TestSuite suite() {
return suite(SingleProject.class);
}
}
// constexpr int f() {
// int arr[]{8, 9};
// auto [first, second] = arr;
// return first;
// }
// constexpr int x = f();
public void testBindingFirstElementOfArray() throws Exception {
assertEvaluationEquals(8);
}
// constexpr int f() {
// int arr[]{8, 9};
// auto [first, second] = arr;
// return second;
// }
// constexpr int x = f();
public void testBindingSecondElementOfArray() throws Exception {
assertEvaluationEquals(9);
}
// constexpr int f() {
// int arr[]{8, 9};
// auto [first, second, third] = arr;
// return third;
// }
// constexpr int x = f();
public void testBindingOutOfBoundElementOfArray() throws Exception {
assertEvaluationProblem();
}
// struct Pair {
// int i;
// double d;
// } p{42, 5.0};
// constexpr auto f() {
// auto [first, second] = p;
// return first;
// }
// constexpr auto x = f();
public void testBindingFirstMemberOfObject() throws Exception {
assertEvaluationEquals(42);
}
// struct Pair {
// int i;
// double d;
// } p{42, 5.0};
// constexpr auto f() {
// auto [first, second] = p;
// return second;
// }
// constexpr auto x = f();
public void testBindingSecondMemberOfObject() throws Exception {
assertEvaluationEquals(5.0);
}
// struct Base {
// int i;
// };
// struct Sub : Base {
// } s{5};
// auto [inherited] = s;
// auto x = inherited;
public void testBindingInheritedMember() throws Exception {
assertEvaluationEquals(5);
}
// struct Mono {
// int i;
// } p{42};
// constexpr auto f() {
// auto [first, second] = p;
// return second;
// }
// constexpr auto x = f();
public void testBindingOutOfBoundElementOfObject() throws Exception {
assertEvaluationProblem();
}
// constexpr auto f() {
// auto [first, second];
// return second;
// }
// constexpr auto x = f();
public void testUninitializedStructuredBinding() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return t;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeObjectWithMemberGet() throws Exception {
assertEvaluationEquals(3);
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// template <size_t I, typename T, size_t N>
// constexpr auto get(std::array<T, N> const & values) {
// return values.elements[I];
// }
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return t;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeObjectWithFreeGet() throws Exception {
assertEvaluationEquals(3);
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 2> values{{1, 2}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return t;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeObjectWithTooFewElements() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s] = createValues();
// return f;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeObjectWithTooManyElements() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// static const size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return s;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeValueMemberIsStaticConst() throws Exception {
assertEvaluationEquals(2);
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return f;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeValueMemberIsNonConstexpr() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return f;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeValueMemberIsNonStatic() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static double value = static_cast<double>(N);
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return f;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeValueMemberIsNonIntegral() throws Exception {
assertEvaluationProblem();
}
// namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// template <size_t I>
// constexpr auto get() {
// return elements[I];
// }
// };
// size_t nonConstexprFunction() {
// return 3;
// }
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// static const size_t value = nonConstexprFunction();
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
// }
// constexpr auto createValues() {
// std::array<int, 3> values{{1, 2, 3}};
// return values;
// }
// constexpr auto foo() {
// auto [f, s, t] = createValues();
// return s;
// }
// constexpr auto x = foo();
public void testBindingOutOfTupleLikeValueMemberWithNonConstexprInitialization() throws Exception {
assertEvaluationProblem();
}
}

View file

@ -41,6 +41,7 @@ import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser;
import org.eclipse.cdt.internal.core.dom.parser.CStringValue;
import org.eclipse.cdt.internal.core.dom.parser.FloatingPointValue;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.GNUCPPSourceParser;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
@ -63,6 +64,11 @@ public class TestBase extends IndexBindingResolutionTestBase {
return map;
}
protected void assertEvaluationProblem() throws Exception {
IValue value = getValue();
assertTrue(IntegralValue.ERROR.equals(value) || IntegralValue.UNKNOWN.equals(value));
}
protected void assertEvaluationEquals(boolean expectedValue) throws Exception {
IValue value = getValue();
Number num = value.numberValue();

View file

@ -0,0 +1,295 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx17;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.char_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.double_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.int_;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.internal.index.tests.IndexBindingResolutionTestBase;
public class StructuredBindingIndexTests extends IndexBindingResolutionTestBase {
public StructuredBindingIndexTests() {
setStrategy(new SinglePDOMTestStrategy(true));
}
//struct S {
// int i;
//} s{};
//auto [z] = s;
public void testLocalStructuredBindingFromMemberOfBasicType() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("z"), int_);
}
//struct S {
// int i;
//} s{};
//auto [z] = s;
//auto x = z;
public void testExternalStructuredBindingFromMemberOfBasicType() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("z"), int_);
}
//struct T {
//};
//struct S {
// T t;
//} s{};
//auto [z] = s;
//T localT{};
public void testLocalStructuredBindingFromMemberOfUserDefinedType() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
IVariable variable = helper.assertNonProblem("z");
IType variableType = variable.getType();
IVariable localT = helper.assertNonProblem("localT");
IType typeT = localT.getType();
assertSameType(typeT, variableType);
}
//struct T {
//};
//struct S {
// T t;
//} s{};
//auto [z] = s;
//auto x = z;
//T localT{};
public void testExternalStructuredBindingFromMemberOfUserDefinedType() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
IVariable variable = helper.assertNonProblem("z");
IType variableType = variable.getType();
IVariable localT = helper.assertNonProblem("localT");
IType typeT = localT.getType();
assertSameType(typeT, variableType);
}
//struct T {
//};
//struct Base1 {
//};
//struct Base2 {
// T t;
// int i;
// double d;
// char c;
//};
//struct S : Base1, Base2 {
//} s{};
//auto [t, i, d, c] = s;
//T localT{};
public void testMultipleVariablesInStructuredBindingFromMembers() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
IVariable variableT = helper.assertNonProblem("t,", 1);
IType variableTType = variableT.getType();
IVariable localT = helper.assertNonProblem("localT");
IType typeT = localT.getType();
assertSameType(typeT, variableTType);
assertType(helper.assertNonProblem("i,", 1), int_);
assertType(helper.assertNonProblem("d,", 1), double_);
assertType(helper.assertNonProblem("c]", 1), char_);
}
//struct T {
//};
//struct Base1 {
// T t;
// int i;
// double d;
// char c;
//};
//struct Base2 : Base1 {
//};
//struct S : Base2 {
//} s{};
//auto [t, i, d, c] = s;
//T localT{};
public void testMultipleVariablesDeepBaseStructure() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
IVariable variableT = helper.assertNonProblem("t,", 1);
IType variableTType = variableT.getType();
IVariable localT = helper.assertNonProblem("localT");
IType typeT = localT.getType();
assertSameType(typeT, variableTType);
assertType(helper.assertNonProblem("i,", 1), int_);
assertType(helper.assertNonProblem("d,", 1), double_);
assertType(helper.assertNonProblem("c]", 1), char_);
}
//struct S {
// int i;
// static float f;
// double d;
//} s{};
//auto [i, d] = s;
public void testStaticFieldsAreNotConsidered() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("i,", 1), int_);
assertType(helper.assertNonProblem("d]", 1), double_);
}
//namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
//}
//auto [f, s, t] = std::array<int, 3>{1, 2, 3};
public void testStandardArray() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
//Target code lacks the required get() functions determine the value of the names f, s and t.
//But the types can still be resolved with tuple_element
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s,", 1), int_);
assertType(helper.assertNonProblem("t]", 1), int_);
}
//namespace std {
// using size_t = unsigned long long;
// template <typename T1, typename T2>
// struct pair {
// T1 t1;
// T2 t2;
// };
// template <typename T>
// struct tuple_size;
// template <typename T1, typename T2>
// struct tuple_size<pair<T1, T2>> {
// constexpr static size_t value = 2;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <typename T>
// struct tuple_element_base {
// using type = T;
// };
// template <typename T1, typename T2>
// struct tuple_element<0, pair<T1, T2>> : tuple_element_base<T1> {
// };
// template <typename T1, typename T2>
// struct tuple_element<1, pair<T1, T2>> : tuple_element_base<T2> {
// };
// template <size_t I, typename T1, typename T2>
// auto get(pair<T1, T2> const & p) {
// if constexpr (I == 0) {
// return p.t1;
// } else {
// return p.t2;
// }
// }
//}
//auto [a, b] = std::pair<int, double>{42, 3.14};
public void testRecursiveTupleElement() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("a,", 1), int_);
assertType(helper.assertNonProblem("b]", 1), double_);
}
//namespace std {
// using size_t = unsigned long long;
// template <typename T, size_t N>
// struct array {
// T elements[N];
// };
// template <typename T>
// struct tuple_size;
// template <typename T, size_t N>
// struct tuple_size<array<T, N>> {
// constexpr static size_t value = N;
// };
// template <size_t I, typename T>
// struct tuple_element;
// template <size_t I, typename T, size_t N>
// struct tuple_element<I, array<T, N>> {
// using type = T;
// };
//}
//struct X {
// int first;
// int second;
//};
//int main() {
// auto arr = std::array<X, 3>{X{1,2}, X{3,4}, X{5,6}};
// for (auto [firstX, secondX] : arr.elements) {
// auto sum = firstX + secondX;
// }
//}
public void testStandardArrayInLoop() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
//Target code lacks the required get() functions determine the value of the names f, s and t.
//But the types can still be resolved with tuple_element
assertType(helper.assertNonProblem("firstX,", 6), int_);
assertType(helper.assertNonProblem("secondX]", 7), int_);
assertType(helper.assertNonProblem("sum", 3), int_);
}
//struct X {
// int first;
// int second;
// void fun();
//};
//void X::fun() {
// auto [f, s] = *this;
//}
public void testBindStarThis() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), int_);
}
}

View file

@ -0,0 +1,418 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx17;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.char_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.constInt;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.double_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.float_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.int_;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.referenceTo;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.referenceToConstInt;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.referenceToInt;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.rvalueReferenceTo;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.rvalueReferenceToInt;
import static org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes.volatileOf;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase;
import org.eclipse.cdt.core.parser.tests.ast2.CommonCPPTypes;
import org.eclipse.cdt.internal.core.dom.parser.CompositeValue;
import org.eclipse.cdt.internal.core.dom.parser.FloatingPointValue;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPStructuredBindingComposite;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
public class StructuredBindingTests extends AST2CPPTestBase {
//struct S {
// int first;
// double second;
//};
//auto [f1, s1] = S{1, 2};
//auto f2 = f1;
//auto s2 = s1;
public void testFromTemporary() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
IBinding f1Declaration = helper.assertNonProblem("f1, ", 2);
IBinding f1Reference = helper.assertNonProblem("f1;", 2);
assertSame(f1Declaration, f1Reference);
IBinding s1Declaration = helper.assertNonProblem("s1] ", 2);
IBinding s1Reference = helper.assertNonProblem("s1;", 2);
assertSame(s1Declaration, s1Reference);
}
//struct S {
// int first;
// double second;
//};
//S createS() {
// return {1, 2};
//}
//auto [f, s] = createS();
public void testFromReturnValue() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//struct S {
// int first;
// double second;
//};
//S createS() {
// return {1, 2};
//}
//auto [f, s]{createS()};
public void testBracedInitialization() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//struct S {
// int first;
// double second;
//};
//S createS() {
// return {1, 2};
//}
//auto [f, s](createS());
public void testCopyInitialization() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//struct S {
// int first;
// int second;
// float third;
// double fourth;
// char fifth;
//};
//auto [f, s, t, fo, fif] = S{1, 2, 1.5f, 3.1415, '*'};
public void testWithManyInitializers() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s,", 1), int_);
assertType(helper.assertNonProblem("t,", 1), float_);
assertType(helper.assertNonProblem("fo,", 2), double_);
assertType(helper.assertNonProblem("fif]", 3), char_);
}
//struct Base {
// int bi;
//};
//struct Sub : Base {
// static double sd;
//};
//auto [b] = Sub{1};
public void testWithBaseClass() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("b]", 1), int_);
}
//auto f() -> int(&)[2];
//auto [x, y] = f();
//auto & [xr, yr] = f();
public void testStandardExample1() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("xr,", 2), referenceToInt);
assertType(helper.assertNonProblem("yr]", 2), referenceToInt);
}
//struct S {
// int x1 : 2;
// volatile double y1;
//};
//S createS();
//auto const [x, y] = createS();
public void testStandardExample2() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("x,", 1), constInt);
assertType(helper.assertNonProblem("y]", 1), CommonCPPTypes.constVolatileOf(double_));
}
//int arr[]{1, 2, 3};
//auto [f, s, t] = arr;
public void testFromArray() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s,", 1), int_);
assertType(helper.assertNonProblem("t]", 1), int_);
}
//struct S {
// int i;
//} s{};
//auto && [f] = s;
public void testForwardingReferenceWithLvalue() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f]", 1), referenceToInt);
}
//struct S {
// int i;
//} s{};
//auto && [f] = static_cast<S&&>(s);
public void testForwardingReferenceWithXvalue() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f]", 1), rvalueReferenceToInt);
}
//struct S {
// int i;
//};
//auto && [f] = S{};
public void testForwardingReferenceWithRvalue() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f]", 1), rvalueReferenceToInt);
}
//struct S {
// int first;
// double second;
//};
//
//namespace std {
// template <typename>
// struct tuple_size;
//}
//auto [f, s] = S{};
public void testUnspecializedTupleSizeTemplate() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//namespace std {
// using size_t = unsigned long long;
//}
//
//struct S {
// int first() const {
// return 1;
// }
// double second() const {
// return 2.0;
// }
// template <std::size_t V>
// auto get() {
// if constexpr (V == 0) {
// return first();
// } else if (V == 1) {
// return second();
// }
// static_assert(V < 2);
// }
//};
//
//namespace std {
// template <typename>
// struct tuple_size;
// template <>
// struct tuple_size<S> {
// constexpr static size_t value = 2;
// };
// template <std::size_t, typename>
// struct tuple_element;
// template <>
// struct tuple_element<0, S> {
// using type = int;
// };
// template <>
// struct tuple_element<1, S> {
// using type = double;
// };
//}
//auto [f, s] = S{};
public void testFromTupleLikeDecompositionWithMemberGet() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//namespace std {
// using size_t = unsigned long long;
//}
//
//struct S {
// int first() const {
// return 1;
// }
// double second() const {
// return 2.0;
// }
//};
//template <std::size_t V>
//auto get(S s) {
// if constexpr (V == 0) {
// return s.first();
// } else if (V == 1) {
// return s.second();
// }
// static_assert(V < 2);
//}
//
//namespace std {
// template <typename>
// struct tuple_size;
// template <>
// struct tuple_size<S> {
// constexpr static size_t value = 2;
// };
// template <std::size_t, typename>
// struct tuple_element;
// template <>
// struct tuple_element<0, S> {
// using type = int;
// };
// template <>
// struct tuple_element<1, S> {
// using type = double;
// };
//}
//auto [f, s] = S{};
public void testFromTupleLikeDecompositionWithInheritedTupleElementType() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("f,", 1), int_);
assertType(helper.assertNonProblem("s]", 1), double_);
}
//struct S {
// int member;
//} s{1};
//auto [valueLarg] = s;
//auto [valueRarg] = S{1};
//auto const [valueConstLarg] = s;
//auto const [valueConstRarg] = S{1};
//auto & [lrefLarg] = s;
//auto & [lrefRarg] = S{1};
//auto const & [lrefConstLarg] = s;
//auto const & [lrefConstRarg] = S{1};
//auto && [frefLarg] = s;
//auto && [frefRarg] = S{1};
//auto const && [rrefConstLarg] = s;
//auto const && [rrefConstRarg] = S{1};
//auto const sConst = s;
//auto & [lrefLConstarg] = sConst;
//auto && [frefLConstarg] = sConst;
//S volatile sVolatile{1};
//auto & [lrefLVolatilearg] = sVolatile;
//auto && [frefLVolatilearg] = sVolatile;
public void testResultingTypes() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("valueLarg"), int_);
assertType(helper.assertNonProblem("valueRarg"), int_);
assertType(helper.assertNonProblem("valueConstLarg"), constInt);
assertType(helper.assertNonProblem("valueConstRarg"), constInt);
assertType(helper.assertNonProblem("lrefLarg"), referenceToInt);
assertType(helper.assertNonProblem("lrefRarg"), referenceToInt);
assertType(helper.assertNonProblem("lrefConstLarg"), referenceToConstInt);
assertType(helper.assertNonProblem("lrefConstRarg"), referenceToConstInt);
assertType(helper.assertNonProblem("frefLarg"), referenceToInt);
assertType(helper.assertNonProblem("frefRarg"), rvalueReferenceToInt);
assertType(helper.assertNonProblem("rrefConstLarg"), rvalueReferenceTo(constInt));
assertType(helper.assertNonProblem("rrefConstRarg"), rvalueReferenceTo(constInt));
assertType(helper.assertNonProblem("lrefLConstarg"), referenceToConstInt);
assertType(helper.assertNonProblem("frefLConstarg"), referenceToConstInt);
assertType(helper.assertNonProblem("lrefLVolatilearg"), referenceTo(volatileOf(int_)));
assertType(helper.assertNonProblem("frefLVolatilearg"), referenceTo(volatileOf(int_)));
}
//struct Aggregate {
// int i;
// double d;
// auto first() {
// auto [field1, _] = *this;
// return field1;
// }
// auto second() {
// auto [_, field2] = *this;
// return field2;
// }
//};
public void testThisDecomposition() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
assertType(helper.assertNonProblem("field1;", 6), int_);
assertType(helper.assertNonProblem("field2;", 6), double_);
}
//struct S {
// int first;
// double second;
//};
//constexpr S createS() {
// return S{1, 2.0};
//}
//auto [f, s] = createS();
public void testIVariablePropertiesOfImplicitNameForInitializer() throws Exception {
parseAndCheckBindings();
BindingAssertionHelper helper = getAssertionHelper();
ICPPClassType classS = helper.assertNonProblem("S", 1);
IASTImplicitName[] implicitNames = helper.getImplicitNames("= createS();", 11);
assertEquals(1, implicitNames.length);
IASTImplicitName implicitName = implicitNames[0];
IBinding binding = implicitName.getBinding();
CPPStructuredBindingComposite variable = assertInstance(binding, CPPStructuredBindingComposite.class);
assertType(variable, classS);
IValue initialValue = variable.getInitialValue();
CompositeValue compositeValue = assertInstance(initialValue, CompositeValue.class);
ICPPEvaluation[] subvalues = compositeValue.getAllSubValues();
assertEquals(2, subvalues.length);
assertEquals(IntegralValue.create(1), subvalues[0].getValue());
assertEquals(FloatingPointValue.create(2.0), subvalues[1].getValue());
}
}

View file

@ -16,6 +16,7 @@ package org.eclipse.cdt.internal.index.tests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx14.GenericLambdaIndexTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx14.ReturnTypeDeductionIndexTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx17.StructuredBindingIndexTests;
import org.eclipse.cdt.core.parser.tests.ast2.cxx17.TemplateAutoIndexTests;
import junit.framework.Test;
@ -48,6 +49,7 @@ public class IndexTests extends TestSuite {
// C++17 index test suites
suite.addTestSuite(TemplateAutoIndexTests.class);
suite.addTestSuite(StructuredBindingIndexTests.class);
IndexCPPBindingResolutionBugs.addTests(suite);
IndexCPPBindingResolutionTest.addTests(suite);

View file

@ -376,4 +376,9 @@ int __declspec(selectany)* pi2 = 0;
//!MS declspec attribute on simple declaration
//%CPP GNU
__declspec(thread) int tls_i = 1;
__declspec(thread) int tls_i = 1;
//!Attributed structured binding declaration
//%CPP
int arr[]{1, 2, 3};
[[nodiscard]] auto [a, b, c] = arr;

View file

@ -214,3 +214,17 @@ enum struct [[foo]] X{ };
//!Attributed scoped enum declaration with keyword class
//%CPP
enum class [[foo]] X{ };
//!Structured binding declarations
//%CPP
struct S
{
int first, second, third;
} s{};
int arr[]{1, 2, 3};
auto [a, b, c] = arr;
auto [d, e, f]{s};
auto [g, h, i](arr);
auto& [j, k, l] = arr;
const auto& [m, n, o] = S{};
auto&& [p, q, r] = S{};

View file

@ -421,4 +421,19 @@ void f()
case 42:
break;
}
}
//!Range-based for with structured binding
//%CPP
struct S
{
int i;
float f;
};
void rangedBasedForContext()
{
S esses[] = {S{}, S{}, S{}};
for (auto [l, r]: esses){
}
}

View file

@ -129,7 +129,8 @@ Require-Bundle: org.eclipse.cdt.core.native;bundle-version="[5.10.1,6.0.0)";visi
org.eclipse.ltk.core.refactoring;bundle-version="[3.10.100,4.0.0)",
org.eclipse.text;bundle-version="[3.8.200,4.0.0)",
com.ibm.icu;bundle-version="4.4.2",
com.google.gson
com.google.gson,
org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: javax.xml.bind;version="2.2.0"

View file

@ -50,6 +50,8 @@ public interface ISemanticProblem {
int TYPE_CANNOT_DEDUCE_DECLTYPE_AUTO_TYPE = 10007;
/** @since 6.3 */
int TYPE_AUTO_FOR_VIRTUAL_METHOD = 10008;
/** @since 6.9 */
int TYPE_CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE = 10009;
/**
* Returns the ID of the problem.

View file

@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.dom.ast.cpp;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.jdt.annotation.Nullable;
/**
* This is a structured binding declaration which contains a sequence names,
* in square brackets, that decompose an initializer.
* <p>
* Examples:
* <ul>
* <li><code>auto [x, y]{coordinate};</code></li>
* <li><code>auto & [x, y](coordinate);</code></li>
* <li><code>auto && [x, y] = createCoordinte();</code></li>
* </ul>
*
* @since 6.9
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
*/
public interface ICPPASTStructuredBindingDeclaration
extends IASTSimpleDeclaration, IASTNameOwner, IASTImplicitNameOwner {
/**
* <code>IDENTIFIER</code> represents the relationship between an
* <code>ICPPASTStructuredBindingDeclaration</code> and its
* <code>IASTName</code>s.
*/
public static final ASTNodeProperty IDENTIFIER = new ASTNodeProperty(
"ICPPASTStructuredBindingDeclaration.IDENTIFIER - IASTName for ICPPASTStructuredBindingDeclaration"); //$NON-NLS-1$
/**
* <code>INITIALIZER</code> represents the relationship between an
* <code>ICPPASTStructuredBindingDeclaration</code> and its <code>IASTInitializer</code>.
*/
public static final ASTNodeProperty INITIALIZER = new ASTNodeProperty(
"ICPPASTStructuredBindingDeclaration.INITIALIZER - IASTInitializer for ICPPASTStructuredBindingDeclaration"); //$NON-NLS-1$
/**
* Returns the <code>RefQualifier</code> of the structured binding. For either lvalue or
* rvalue reference qualifiers.
* <p>
* Examples:
* <ul>
* <li>For <code>auto [x, y] = coordinate;</code> it returns the empty <code>Optional</code></li>
* <li>For <code>auto & [x, y] = coordinate;</code> it returns <code>Optional.of(RefQualifier.LVALUE)</code></li>
* <li>For <code>auto && [x, y] = createCoordinte();</code> it returns <code>Optional.of(RefQualifier.RVALUE)</code></li>
* </ul>
*
* @return The returned <code>RefQualifier</code> of the C++ declaration or <code>null</code> if there is no reference qualifier.
* if the structured binding does not have a reference qualifier.
* @see RefQualifier
*/
@Nullable
RefQualifier getRefQualifier();
/**
* Returns the list of names declared by this structured binding declaration.
* <p>
* Example: For <code>auto & [x, y] = coordinate;</code> it returns the names <code>x</code> and <code>y</code>.
*
* @return All declared names of the structured binding as<code>IASTName[]</code>
* @see IASTName
*/
IASTName[] getNames();
/**
* Returns the initializer of the structured binding declaration.
*
* This will not be present if the structured binding is part of a range-based for loop.
*
* * Examples:
* <ul>
* <li>For <code>auto [x, y]{coordinate};</code> it returns an <code>ICPPASTInitializerList</code></li>
* <li>For <code>auto & [x, y](coordinate);</code> it returns an <code>ICPPASTConstructorInitializer</code></li>
* <li>For <code>auto && [x, y] = createCoordinte();</code> it returns an <code>IASTEqualsInitializer</code></li>
* </ul>
*
* @return The <code>IASTInitializer</code> of this structured binding. It can be <code>null</code> if the C++ declaration is lacking an initializer.
* @see IASTInitializer
* @see ICPPASTInitializerList
* @see ICPPASTConstructorInitializer
* @see IASTEqualsInitializer
*/
@Nullable
IASTInitializer getInitializer();
}

View file

@ -34,6 +34,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier.ScopeStyle;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUnaryTypeTransformation.Operator;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.IGPPASTArrayRangeDesignator;
import org.eclipse.cdt.core.dom.parser.cpp.ICPPASTAttributeSpecifier;
@ -503,4 +504,15 @@ public interface ICPPNodeFactory extends INodeFactory {
*/
@Deprecated
public ICPPASTAttributeSpecifier newAttributeSpecifier();
/**
* @since 6.9
*/
public ICPPASTStructuredBindingDeclaration newStructuredBindingDeclaration();
/**
* @since 6.9
*/
public ICPPASTStructuredBindingDeclaration newStructuredBindingDeclaration(ICPPASTSimpleDeclSpecifier declSpecifier,
RefQualifier refQualifier, IASTName[] names, IASTInitializer initializer);
}

View file

@ -132,6 +132,8 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
public IASTDeclarator fDtor2;
public IToken fDtorToken1;
public boolean isAtStartOfStructuredBinding = false;
public Decl set(IASTDeclSpecifier declspec, IASTDeclarator dtor, IToken dtorToken) {
fDeclSpec1 = declspec;
fDtor1 = dtor;
@ -1675,6 +1677,24 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
return declSpecifierSequence_initDeclarator(option, acceptCompoundWithoutDtor, null);
}
/**
* Checks whether the type specified by the {@code declSpecifier} is "auto" and
* the subsequent token, when looking beyond potential ref-qualifiers (& and &&),
* will be a single opening bracket ([) followed by an identifier.
*/
protected boolean isAtStartOfStructuredBinding(IASTDeclSpecifier declSpecifier) {
if (!isAutoType(declSpecifier)) {
return false;
}
int expectedBracketOffset = 1;
int nextToken = LTcatchEOF(expectedBracketOffset);
if (nextToken == IToken.tAMPER || nextToken == IToken.tAND) {
expectedBracketOffset++;
}
return LTcatchEOF(expectedBracketOffset) == IToken.tLBRACKET
&& LTcatchEOF(expectedBracketOffset + 1) == IToken.tIDENTIFIER;
}
/**
* Parses for two alternatives of a declspec sequence followed by a initDeclarator.
* A second alternative is accepted only, if it ends at the same point of the first alternative. Otherwise the
@ -1689,6 +1709,12 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
if (lt1 == IToken.tEOC)
return result;
// support for structured bindings
if (isAtStartOfStructuredBinding(result.fDeclSpec1)) {
result.isAtStartOfStructuredBinding = true;
return result;
}
// support simple declarations without declarators
final boolean acceptEmpty = acceptCompoundWithoutDtor && isLegalWithoutDtor(result.fDeclSpec1);
if (acceptEmpty) {
@ -2056,6 +2082,14 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
return false;
}
protected static boolean isAutoType(IASTDeclSpecifier declSpec) {
if (declSpec instanceof IASTSimpleDeclSpecifier) {
IASTSimpleDeclSpecifier simpleDeclSpecifier = (IASTSimpleDeclSpecifier) declSpec;
return simpleDeclSpecifier.getType() == IASTSimpleDeclSpecifier.t_auto;
}
return false;
}
protected abstract IASTAmbiguousStatement createAmbiguousStatement();
protected IASTStatement parseLabelStatement() throws EndOfFileException, BacktrackException {

View file

@ -52,7 +52,7 @@ public interface ITypeMarshalBuffer {
EXEC_DECLARATION_STATEMENT = 0x05, EXEC_DECLARATOR = 0x06, EXEC_DEFAULT = 0x07,
EXEC_SIMPLE_DECLARATION = 0x08, EXEC_RETURN = 0x09, EXEC_EXPRESSION_STATEMENT = 0x0A, EXEC_IF = 0x0B,
EXEC_WHILE = 0x0C, EXEC_DO = 0x0D, EXEC_FOR = 0x0E, EXEC_RANGE_BASED_FOR = 0x0F, EXEC_SWITCH = 0x10,
EXEC_CONSTRUCTOR_CHAIN = 0x11;
EXEC_CONSTRUCTOR_CHAIN = 0x11, EXEC_INCOMPLETE = 0x12;
// Can add more executions up to 0x1C, after that it will collide with TypeMarshalBuffer.UNSTORABLE_TYPE.
static final short KIND_MASK = 0x001F;

View file

@ -27,6 +27,8 @@ public class ProblemType implements IProblemType, ISerializableType {
public static final IType AUTO_FOR_VIRTUAL_METHOD = new ProblemType(TYPE_AUTO_FOR_VIRTUAL_METHOD);
public static final IType CANNOT_DEDUCE_AUTO_TYPE = new ProblemType(TYPE_CANNOT_DEDUCE_AUTO_TYPE);
public static final IType CANNOT_DEDUCE_DECLTYPE_AUTO_TYPE = new ProblemType(TYPE_CANNOT_DEDUCE_DECLTYPE_AUTO_TYPE);
public static final IType CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE = new ProblemType(
TYPE_CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE);
public static final IType ENUMERATION_EXPECTED = new ProblemType(TYPE_ENUMERATION_EXPECTED);
public static final IType NO_NAME = new ProblemType(TYPE_NO_NAME);
public static final IType NOT_PERSISTED = new ProblemType(TYPE_NOT_PERSISTED);

View file

@ -51,8 +51,10 @@ import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.gnu.IGNUASTCompoundStatementExpression;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
@ -96,6 +98,8 @@ public abstract class VariableReadWriteFlags {
if (binding instanceof IVariable) {
return rwAssignmentToType(((IVariable) binding).getType(), indirection);
}
} else if (grand instanceof ICPPASTStructuredBindingDeclaration) {
return rwInStructuredBinding((ICPPASTStructuredBindingDeclaration) grand);
}
return READ | WRITE; // fallback
}
@ -113,10 +117,21 @@ public abstract class VariableReadWriteFlags {
}
}
}
} else if (grand instanceof ICPPASTStructuredBindingDeclaration) {
return rwInStructuredBinding((ICPPASTStructuredBindingDeclaration) grand);
}
return READ | WRITE; // fallback
}
protected int rwInStructuredBinding(ICPPASTStructuredBindingDeclaration declaration) {
RefQualifier refQualifier = declaration.getRefQualifier();
if (refQualifier != null && !declaration.getDeclSpecifier().isConst()) {
return READ | WRITE;
} else {
return READ;
}
}
protected int rwInExpression(IASTExpression expr, IASTNode node, int indirection) {
if (expr instanceof IASTIdExpression) {
return rwAnyNode(expr, indirection);

View file

@ -22,6 +22,7 @@ import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.Keywords;
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.ASTNodeSearch;
@ -35,6 +36,10 @@ public class CPPASTImplicitName extends CPPASTName implements IASTImplicitName {
private boolean isOperator;
private boolean isDefinition;
public CPPASTImplicitName(IASTNode parent) {
this(CharArrayUtils.EMPTY, parent);
}
public CPPASTImplicitName(char[] name, IASTNode parent) {
super(name);
setParent(parent);

View file

@ -46,11 +46,15 @@ public class CPPASTSimpleDeclaration extends CPPASTAttributeOwner implements IAS
@Override
public CPPASTSimpleDeclaration copy(CopyStyle style) {
CPPASTSimpleDeclaration copy = new CPPASTSimpleDeclaration();
return copy(copy, style);
}
protected <T extends CPPASTSimpleDeclaration> T copy(T copy, CopyStyle style) {
copy.setDeclSpecifier(declSpecifier == null ? null : declSpecifier.copy(style));
for (IASTDeclarator declarator : getDeclarators()) {
copy.addDeclarator(declarator == null ? null : declarator.copy(style));
}
return copy(copy, style);
return super.copy(copy, style);
}
@Override

View file

@ -0,0 +1,210 @@
/*******************************************************************************
* Copyright (c) 2018 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import java.util.Arrays;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecIncomplete;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecSimpleDeclaration;
import org.eclipse.jdt.annotation.NonNull;
public class CPPASTStructuredBindingDeclaration extends CPPASTSimpleDeclaration
implements ICPPASTStructuredBindingDeclaration {
private RefQualifier refQualifier;
private IASTName[] names;
private IASTInitializer initializer;
private IASTImplicitName implicitInitializerName;
public CPPASTStructuredBindingDeclaration() {
}
public CPPASTStructuredBindingDeclaration(IASTDeclSpecifier declSpecifier, RefQualifier refQualifier,
IASTName[] names, IASTInitializer initializer) {
super(declSpecifier);
this.refQualifier = refQualifier;
for (IASTName name : names) {
addName(name);
}
setInitializer(initializer);
}
@Override
public RefQualifier getRefQualifier() {
return refQualifier;
}
public void setRefQualifier(RefQualifier refQualifier) {
assertNotFrozen();
this.refQualifier = refQualifier;
}
@Override
public IASTName[] getNames() {
if (names == null) {
return IASTName.EMPTY_NAME_ARRAY;
}
names = ArrayUtil.trim(names);
return names;
}
@Override
public IASTInitializer getInitializer() {
return initializer;
}
@Override
public boolean accept(ASTVisitor action) {
if (action.shouldVisitDeclarations) {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT:
return false;
case ASTVisitor.PROCESS_SKIP:
return true;
default:
break;
}
}
IASTDeclSpecifier declSpecifier = getDeclSpecifier();
if (declSpecifier != null && !declSpecifier.accept(action)) {
return false;
}
for (IASTName name : getNames()) {
if (!name.accept(action)) {
return false;
}
}
if (initializer != null && !initializer.accept(action)) {
return false;
}
if (action.shouldVisitImplicitNames && !getImplicitNames()[0].accept(action)) {
return false;
}
if (action.shouldVisitDeclarations) {
switch (action.leave(this)) {
case ASTVisitor.PROCESS_ABORT:
return false;
case ASTVisitor.PROCESS_SKIP:
return true;
default:
break;
}
}
return true;
}
protected void addName(IASTName name) {
assertNotFrozen();
if (name != null) {
names = ArrayUtil.append(IASTName.class, names, name);
name.setParent(this);
name.setPropertyInParent(IDENTIFIER);
}
}
protected void setInitializer(IASTInitializer initializer) {
assertNotFrozen();
if (initializer != null) {
this.initializer = initializer;
initializer.setParent(this);
initializer.setPropertyInParent(INITIALIZER);
}
}
@Override
public CPPASTStructuredBindingDeclaration copy() {
return copy(CopyStyle.withoutLocations);
}
@Override
public CPPASTStructuredBindingDeclaration copy(CopyStyle style) {
CPPASTStructuredBindingDeclaration copy = new CPPASTStructuredBindingDeclaration();
return copy(copy, style);
}
protected <T extends CPPASTStructuredBindingDeclaration> T copy(@NonNull T copy, CopyStyle style) {
copy.setRefQualifier(refQualifier);
if (initializer != null) {
copy.setInitializer(initializer.copy(style));
}
for (IASTName name : names) {
if (name == null) {
break;
}
copy.addName(name.copy(style));
}
return super.copy(copy, style);
}
@Override
public ICPPExecution getExecution() {
IASTName[] names = getNames();
ICPPExecution[] nameExecutions = Arrays.stream(names).map(name -> {
IBinding binding = name.resolveBinding();
if (binding instanceof CPPVariable) {
CPPVariable variable = (CPPVariable) binding;
ICPPEvaluation initializerEval = variable.getInitializerEvaluation();
return new ExecDeclarator((ICPPBinding) binding, initializerEval);
}
return ExecIncomplete.INSTANCE;
}).toArray(ICPPExecution[]::new);
return new ExecSimpleDeclaration(nameExecutions);
}
@Override
public int getRoleForName(IASTName name) {
return r_definition;
}
@Override
public IASTImplicitName[] getImplicitNames() {
if (implicitInitializerName == null) {
ICPPEvaluation initializerEvaluation = CPPVariable.evaluationOfInitializer(initializer);
if (initializerEvaluation == null) {
initializerEvaluation = EvalFixed.INCOMPLETE;
}
CPPASTImplicitName implicitName = new CPPASTImplicitName(this);
implicitName.setIsDefinition(true);
if (initializer != null) {
implicitName.setOffsetAndLength((ASTNode) initializer);
}
implicitInitializerName = implicitName;
CPPStructuredBindingComposite implicitInitializerVariable = new CPPStructuredBindingComposite(
implicitInitializerName, initializerEvaluation);
implicitInitializerName.setBinding(implicitInitializerVariable);
}
return new IASTImplicitName[] { implicitInitializerName };
}
}

View file

@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2019 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
/**
* Represents a variable implicitly created in C++ code.
* For example the initializer of a structured binding decomposition [dcl.struct.bind]:
* <code>auto [first, second] = decomposed;</code>
*
* The <code>initializerEvaluation</code> has to be supplied.
*
*/
public class CPPImplicitVariable extends CPPVariable {
private ICPPEvaluation initializerEvaluation;
public CPPImplicitVariable(IASTImplicitName name, ICPPEvaluation initializerEvaluation) {
super(name);
this.initializerEvaluation = initializerEvaluation;
}
@Override
public ICPPEvaluation getInitializerEvaluation() {
return initializerEvaluation;
}
@Override
public IType getType() {
return initializerEvaluation.getType();
}
@Override
public IValue getInitialValue() {
return initializerEvaluation.getValue();
}
}

View file

@ -91,6 +91,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTIfStatement;
@ -119,6 +120,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStaticAssertDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
@ -914,4 +916,15 @@ public class CPPNodeFactory extends NodeFactory implements ICPPNodeFactory {
public ICPPASTWhileStatement newWhileStatement(IASTExpression condition, IASTStatement body) {
return new CPPASTWhileStatement(condition, body);
}
@Override
public ICPPASTStructuredBindingDeclaration newStructuredBindingDeclaration() {
return new CPPASTStructuredBindingDeclaration();
}
@Override
public ICPPASTStructuredBindingDeclaration newStructuredBindingDeclaration(ICPPASTSimpleDeclSpecifier declSpecifier,
RefQualifier refQualifier, IASTName[] names, IASTInitializer initializer) {
return new CPPASTStructuredBindingDeclaration(declSpecifier, refQualifier, names, initializer);
}
}

View file

@ -0,0 +1,239 @@
/*******************************************************************************
* Copyright (c) 2019 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Thomas Corbat (IFS) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor.IS_STATIC_VARIABLE;
import java.util.Arrays;
import java.util.Optional;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
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.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
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.ICPPTemplateArgument;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinary;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionCall;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalMemberAccess;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.LookupData;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
/**
* Represents the implicit object created for the initializer of a structured binding declaration.
* It is used for caching the evaluation strategy used to determine the <code>ICPPEvaluation</code> of
* the names introduced by the declaration.
*
*/
public class CPPStructuredBindingComposite extends CPPImplicitVariable {
private static final char[] GET_NAME = "get".toCharArray(); //$NON-NLS-1$
private static final IStructuredBindingNameEvaluationStrategy INCOMPLETE_INITIALIZER = new IStructuredBindingNameEvaluationStrategy() {
@Override
public ICPPEvaluation getEvaluation(int nameIndex) {
return EvalFixed.INCOMPLETE;
}
};
private IStructuredBindingNameEvaluationStrategy evaluationStrategy = null;
private ICPPASTStructuredBindingDeclaration declaration;
public CPPStructuredBindingComposite(IASTImplicitName name, ICPPEvaluation initializerEvaluation) {
super(name, initializerEvaluation);
IASTNode nameParent = name.getParent();
assert (nameParent instanceof ICPPASTStructuredBindingDeclaration);
this.declaration = (ICPPASTStructuredBindingDeclaration) nameParent;
}
public ICPPEvaluation getEvaluationForName(IASTName name) {
int index = ArrayUtil.indexOf(declaration.getNames(), name);
return getNameEvaluationStrategy().getEvaluation(index);
}
private IStructuredBindingNameEvaluationStrategy getNameEvaluationStrategy() {
if (evaluationStrategy == null) {
evaluationStrategy = createEvaluationStrategy(declaration);
}
return evaluationStrategy;
}
private IStructuredBindingNameEvaluationStrategy createEvaluationStrategy(
ICPPASTStructuredBindingDeclaration structuredBinding) {
IASTInitializer initializer = structuredBinding.getInitializer();
ICPPEvaluation initializerEvaluation = getInitializerEvaluation();
if (initializer != null && initializerEvaluation != null) {
IType initializerType = getType();
IType unwrappedType = SemanticUtil.getNestedType(initializerType, SemanticUtil.ALLCVQ | SemanticUtil.REF);
if (unwrappedType instanceof IArrayType) {
return new ArrayElement(initializer, initializerEvaluation);
}
if (unwrappedType instanceof ICPPClassType) {
IASTName[] definedNames = structuredBinding.getNames();
long numberOfNames = definedNames.length;
ICPPClassType classType = (ICPPClassType) unwrappedType;
if (numberOfNames > 0) {
IScope scope = CPPSemantics.getLookupScope(definedNames[0]);
Optional<ICPPClassSpecialization> tupleSizeInstance = CPPVisitor
.findTupleSizeWithValueMember(unwrappedType, scope, structuredBinding);
if (tupleSizeInstance.isPresent()) {
if (CPPVisitor.hasConstexprStaticIntegralValueField(tupleSizeInstance.get(), numberOfNames)) {
return new TupleElement(initializer, initializerEvaluation, classType);
}
} else {
return new MemberElement(initializer, initializerEvaluation, classType);
}
}
}
}
return INCOMPLETE_INITIALIZER;
}
private abstract static class IStructuredBindingNameEvaluationStrategy {
abstract public ICPPEvaluation getEvaluation(int nameIndex);
}
/**
* Name evaluation strategy for the case in which the initializer has array type.
*/
private class ArrayElement extends IStructuredBindingNameEvaluationStrategy {
private IASTInitializer initializer;
private ICPPEvaluation initializerEvaluation;
private ArrayElement(IASTInitializer initializer, ICPPEvaluation initializerEvaluation) {
this.initializer = initializer;
this.initializerEvaluation = initializerEvaluation;
}
@Override
public ICPPEvaluation getEvaluation(int nameIndex) {
EvalFixed indexEvaluation = new EvalFixed(CPPBasicType.UNSIGNED_INT, ValueCategory.PRVALUE,
IntegralValue.create(nameIndex));
return new EvalBinary(EvalBinary.op_arrayAccess, initializerEvaluation, indexEvaluation, initializer);
}
}
/**
* Name evaluation strategy for the case in which the initializer has class type for which std::tuple_size is specialized.
*/
private class TupleElement extends IStructuredBindingNameEvaluationStrategy {
private IASTInitializer initializer;
private ICPPEvaluation initializerEvaluation;
private ICPPClassType initializerType;
private TupleElement(IASTInitializer initializer, ICPPEvaluation initializerEvaluation,
ICPPClassType initializerType) {
this.initializer = initializer;
this.initializerEvaluation = initializerEvaluation;
this.initializerType = initializerType;
}
@Override
public ICPPEvaluation getEvaluation(int nameIndex) {
EvalFixed indexEvaluation = new EvalFixed(CPPBasicType.UNSIGNED_INT, ValueCategory.PRVALUE,
IntegralValue.create(nameIndex));
IBinding resolvedFunction = resolveGetFunction(initializerType, initializer, initializerEvaluation,
indexEvaluation);
if (resolvedFunction instanceof ICPPMethod) {
ICPPEvaluation eExpressionEvaluation = new EvalMemberAccess(initializerType,
initializerEvaluation.getValueCategory(), resolvedFunction, initializerEvaluation, false,
initializer);
return new EvalFunctionCall(new ICPPEvaluation[] { eExpressionEvaluation }, null, initializer);
} else if (resolvedFunction instanceof ICPPFunction) {
EvalBinding functionEvaluation = new EvalBinding(resolvedFunction,
((ICPPFunction) resolvedFunction).getType(), initializer);
return new EvalFunctionCall(new ICPPEvaluation[] { functionEvaluation, initializerEvaluation }, null,
initializer);
}
return EvalFixed.INCOMPLETE;
}
/**
*
* Resolves the member or non-member get() function template for a type and given arguments.
*
* Returns {@code null} if the function cannot be resolved
*/
private IBinding resolveGetFunction(ICompositeType classType, IASTNode point, ICPPEvaluation argument,
ICPPEvaluation index) {
//find member and non-member get() bindings in class scope and all parent scopes
IBinding[] allGetBindings = CPPSemantics.findBindings(classType.getCompositeScope(), GET_NAME, false,
point);
ICPPFunction[] functions = Arrays.stream(allGetBindings).filter(IFunction.class::isInstance)
.map(IFunction.class::cast).toArray(ICPPFunction[]::new);
ICPPTemplateArgument[] arguments = new ICPPTemplateArgument[] { new CPPTemplateNonTypeArgument(index) };
LookupData lookupGet = new LookupData(GET_NAME, arguments, point);
//LookupData::containsImpliedObject is ignored for non-member functions in CPPSemantics.resolveFunction
//and will therefore find the the free get function using the arguments
lookupGet.setFunctionArguments(true, argument);
try {
return CPPSemantics.resolveFunction(lookupGet, functions, true, true);
} catch (DOMException e) {
return null;
}
}
}
/**
* Name evaluation strategy for the case in which the initializer has class type with public members.
*/
private class MemberElement extends IStructuredBindingNameEvaluationStrategy {
private IASTInitializer initializer;
private ICPPEvaluation initializerEvaluation;
private ICPPClassType initializerType;
private MemberElement(IASTInitializer initializer, ICPPEvaluation initializerEvaluation,
ICPPClassType initializerType) {
this.initializer = initializer;
this.initializerEvaluation = initializerEvaluation;
this.initializerType = initializerType;
}
@Override
public ICPPEvaluation getEvaluation(int nameIndex) {
IField[] nonStaticFields = Arrays.stream(ClassTypeHelper.getFields(initializerType))
.filter(IS_STATIC_VARIABLE.negate()).toArray(IField[]::new);
//TODO (tcorbat): Further restrictions to structured bindings that are not checked. Maybe add a Codan checker.
// * Add further check that all members origin from the same class/base class
// * classType must not have an anonymous union member
// * Check accessibility of the members
if (nameIndex >= 0 && nameIndex < nonStaticFields.length) {
IField boundField = nonStaticFields[nameIndex];
return new EvalMemberAccess(initializerType, initializerEvaluation.getValueCategory(), boundField,
initializerEvaluation, false, initializer);
}
return EvalFixed.INCOMPLETE;
}
}
}

View file

@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -41,6 +42,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerList;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
@ -52,7 +54,6 @@ import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalConstructor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalUtil;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.core.runtime.PlatformObject;
@ -258,7 +259,7 @@ public class CPPVariable extends PlatformObject implements ICPPInternalDeclaredV
IValue initialValue = null;
final IType nestedType = SemanticUtil.getNestedType(getType(), TDEF | REF | CVTYPE);
if (nestedType instanceof ICPPClassType || (initialValue = VariableHelpers.getInitialValue(fDefinition,
fDeclarations, getType())) == IntegralValue.UNKNOWN) {
fDeclarations, getType())) == IntegralValue.UNKNOWN || getStructuredBindingDeclaration() != null) {
ICPPEvaluation initEval = getInitializerEvaluation();
if (initEval == null) {
return null;
@ -319,21 +320,48 @@ public class CPPVariable extends PlatformObject implements ICPPInternalDeclaredV
if (constructor != null) {
ICPPEvaluation[] arguments = EvalConstructor.extractArguments(initializer, constructor);
return new EvalConstructor(getType(), constructor, arguments, declarator);
} else if (initializer instanceof IASTEqualsInitializer) {
IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer;
ICPPASTInitializerClause clause = (ICPPASTInitializerClause) equalsInitializer.getInitializerClause();
return clause.getEvaluation();
} else if (initializer instanceof ICPPASTInitializerList) {
return ((ICPPASTInitializerClause) initializer).getEvaluation();
} else if (initializer instanceof ICPPASTConstructorInitializer) {
ICPPASTConstructorInitializer ctorInitializer = (ICPPASTConstructorInitializer) initializer;
ICPPASTInitializerClause evalOwner = (ICPPASTInitializerClause) ctorInitializer.getArguments()[0];
return evalOwner.getEvaluation();
} else if (initializer == null) {
return null;
}
return evaluationOfInitializer(initializer);
}
ICPPASTStructuredBindingDeclaration structuredBinding = getStructuredBindingDeclaration();
if (structuredBinding != null) {
return evaluationOfStructuredBindingInitializer(structuredBinding);
}
return null;
}
private ICPPEvaluation evaluationOfStructuredBindingInitializer(
ICPPASTStructuredBindingDeclaration structuredBinding) {
IASTImplicitName implicitName = structuredBinding.getImplicitNames()[0];
CPPStructuredBindingComposite variable = (CPPStructuredBindingComposite) implicitName.getBinding();
return variable.getEvaluationForName(fDefinition);
}
public static ICPPEvaluation evaluationOfInitializer(IASTInitializer initializer) {
if (initializer instanceof IASTEqualsInitializer) {
IASTEqualsInitializer equalsInitializer = (IASTEqualsInitializer) initializer;
ICPPASTInitializerClause clause = (ICPPASTInitializerClause) equalsInitializer.getInitializerClause();
return clause.getEvaluation();
} else if (initializer instanceof ICPPASTInitializerList) {
return ((ICPPASTInitializerClause) initializer).getEvaluation();
} else if (initializer instanceof ICPPASTConstructorInitializer) {
ICPPASTConstructorInitializer ctorInitializer = (ICPPASTConstructorInitializer) initializer;
ICPPASTInitializerClause evalOwner = (ICPPASTInitializerClause) ctorInitializer.getArguments()[0];
return evalOwner.getEvaluation();
}
return null;
}
private ICPPASTStructuredBindingDeclaration getStructuredBindingDeclaration() {
IASTNode definition = getDefinition();
if (definition != null) {
IASTNode parent = definition.getParent();
if (parent instanceof ICPPASTStructuredBindingDeclaration) {
return (ICPPASTStructuredBindingDeclaration) parent;
}
}
return EvalFixed.INCOMPLETE;
return null;
}
private static ICPPConstructor getImplicitlyCalledCtor(ICPPASTDeclarator declarator) {
@ -350,4 +378,5 @@ public class CPPVariable extends PlatformObject implements ICPPInternalDeclaredV
public void allDeclarationsDefinitionsAdded() {
fAllResolved = true;
}
}

View file

@ -134,6 +134,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStaticAssertDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
@ -2886,6 +2887,51 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
return super.isLegalWithoutDtor(declSpec);
}
private RefQualifier optionalRefQualifier() throws EndOfFileException {
int nextToken = LT(1);
switch (nextToken) {
case IToken.tAMPER:
consume();
return RefQualifier.LVALUE;
case IToken.tAND:
consume();
return RefQualifier.RVALUE;
default:
return null;
}
}
private ICPPASTStructuredBindingDeclaration structuredBinding(ICPPASTSimpleDeclSpecifier simpleDeclSpecifier,
List<IASTAttributeSpecifier> attributes) throws BacktrackException, EndOfFileException {
RefQualifier refQualifier = optionalRefQualifier();
consume(IToken.tLBRACKET);
IASTName[] identifiers = identifierList();
int endOffset = consume(IToken.tRBRACKET).getEndOffset();
IASTInitializer initializer = null;
if (LT(1) != IToken.tCOLON) {
switch (LT(1)) {
case IToken.tASSIGN:
initializer = equalsInitalizerClause(false);
break;
case IToken.tLBRACE:
case IToken.tLPAREN:
initializer = bracedOrCtorStyleInitializer();
break;
}
endOffset = consume(IToken.tSEMI).getEndOffset();
}
ICPPASTStructuredBindingDeclaration structuredBinding = getNodeFactory()
.newStructuredBindingDeclaration(simpleDeclSpecifier, refQualifier, identifiers, initializer);
setRange(structuredBinding, simpleDeclSpecifier, endOffset);
addAttributeSpecifiers(attributes, structuredBinding);
return structuredBinding;
}
/**
* Parses a declaration with the given options.
*/
@ -2903,6 +2949,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
IASTDeclSpecifier altDeclSpec = null;
IASTDeclarator altDtor = null;
IToken markBeforDtor = null;
boolean isAtStartOfStructuredBinding = false;
try {
Decl decl = declSpecifierSequence_initDeclarator(declOption, true);
markBeforDtor = decl.fDtorToken1;
@ -2910,6 +2957,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
dtor = decl.fDtor1;
altDeclSpec = decl.fDeclSpec2;
altDtor = decl.fDtor2;
isAtStartOfStructuredBinding = decl.isAtStartOfStructuredBinding;
} catch (FoundAggregateInitializer lie) {
declSpec = lie.fDeclSpec;
// scalability: don't keep references to tokens, initializer may be large
@ -2925,6 +2973,11 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
throw e;
}
if (isAtStartOfStructuredBinding && declSpec instanceof ICPPASTSimpleDeclSpecifier) {
ICPPASTSimpleDeclSpecifier simpleDeclSpecifier = (ICPPASTSimpleDeclSpecifier) declSpec;
return structuredBinding(simpleDeclSpecifier, attributes);
}
IASTDeclarator[] declarators = IASTDeclarator.EMPTY_DECLARATOR_ARRAY;
if (dtor != null) {
declarators = new IASTDeclarator[] { dtor };
@ -4765,17 +4818,10 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
}
// ref-qualifiers
switch (LT(1)) {
case IToken.tAMPER:
fc.setRefQualifier(RefQualifier.LVALUE);
endOffset = consume().getEndOffset();
break;
case IToken.tAND:
fc.setRefQualifier(RefQualifier.RVALUE);
endOffset = consume().getEndOffset();
break;
default:
break;
RefQualifier refQualifier = optionalRefQualifier();
if (refQualifier != null) {
fc.setRefQualifier(refQualifier);
endOffset = getEndOffset();
}
// throws clause
@ -5673,4 +5719,14 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
IASTAlignmentSpecifier typeId) {
return new CPPASTAmbiguousAlignmentSpecifier(expression, typeId);
}
protected IASTName[] identifierList() throws EndOfFileException, BacktrackException {
List<IASTName> result = new ArrayList<>();
result.add(identifier());
while (LT(1) == IToken.tCOMMA) {
consume();
result.add(identifier());
}
return result.toArray(new IASTName[result.size()]);
}
}

View file

@ -31,6 +31,7 @@ import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
@ -120,6 +121,9 @@ public class VariableHelpers {
if (firstCandidate == null) {
firstCandidate = (IArrayType) t;
}
} else if (node instanceof ICPPASTStructuredBindingDeclaration) {
ICPPASTStructuredBindingDeclaration parent = (ICPPASTStructuredBindingDeclaration) node;
return CPPVisitor.createType(parent, n);
}
}
}

View file

@ -141,6 +141,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
@ -938,7 +939,7 @@ public class CPPSemantics {
}
}
static ICPPScope getLookupScope(IASTName name) throws DOMException {
public static ICPPScope getLookupScope(IASTName name) {
IASTNode parent = name.getParent();
IScope scope = null;
if (parent instanceof ICPPASTBaseSpecifier) {
@ -1822,7 +1823,9 @@ public class CPPSemantics {
return;
}
if (declaration instanceof IASTSimpleDeclaration) {
if (declaration instanceof ICPPASTStructuredBindingDeclaration) {
handleStructuredBinding((ICPPASTStructuredBindingDeclaration) declaration, scope);
} else if (declaration instanceof IASTSimpleDeclaration) {
IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration;
ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) simpleDeclaration.getDeclSpecifier();
IASTDeclarator[] declarators = simpleDeclaration.getDeclarators();
@ -1944,6 +1947,12 @@ public class CPPSemantics {
}
}
private static void handleStructuredBinding(ICPPASTStructuredBindingDeclaration structuredBinding, IScope scope) {
for (IASTName name : structuredBinding.getNames()) {
ASTInternal.addName(scope, name);
}
}
private static void handleEnumeration(ICPPASTEnumerationSpecifier enumSpec, IScope enclosingScope) {
// Add unscoped enumerators to the enclosing scope
if (!enumSpec.isScoped()) {
@ -2647,6 +2656,11 @@ public class CPPSemantics {
return result;
}
/**
* Resolves the {@code IBinding} for given {@code LookupData} in the set of function bindings {@code fns}.
* It supports mixed member/non-member lookup. {@code LookupData::argContainsImpliedObject} is ignored for
* non-member lookup.
*/
public static IBinding resolveFunction(LookupData data, ICPPFunction[] fns, boolean allowUDC,
boolean resolveTargetedArgumentTypes) throws DOMException {
final IASTName lookupName = data.getLookupName();
@ -4262,6 +4276,14 @@ public class CPPSemantics {
* in the given scope.
*/
public static IBinding[] findBindingsForQualifiedName(IScope scope, String qualifiedName) {
return findBindingsForQualifiedName(scope, qualifiedName, null);
}
/**
* Uses C++ lookup semantics to find the possible bindings for the given qualified name starting
* in the given scope.
*/
public static IBinding[] findBindingsForQualifiedName(IScope scope, String qualifiedName, IASTNode beforeNode) {
// Return immediately if the qualifiedName does not match a known format.
Matcher m = QUALNAME_REGEX.matcher(qualifiedName);
if (!m.matches())
@ -4284,7 +4306,7 @@ public class CPPSemantics {
Set<IBinding> bindings = new HashSet<>();
// Look for the name in the given scope.
findBindingsForQualifiedName(scope, qualifiedName, bindings);
findBindingsForQualifiedName(scope, qualifiedName, bindings, beforeNode);
// If the qualified name is not rooted in the global namespace (with a leading ::), then
// look at all parent scopes.
@ -4293,7 +4315,7 @@ public class CPPSemantics {
while (scope != null) {
scope = scope.getParent();
if (scope != null)
findBindingsForQualifiedName(scope, qualifiedName, bindings);
findBindingsForQualifiedName(scope, qualifiedName, bindings, beforeNode);
}
} catch (DOMException e) {
CCorePlugin.log(e);
@ -4303,8 +4325,8 @@ public class CPPSemantics {
return bindings.size() == 0 ? IBinding.EMPTY_BINDING_ARRAY : bindings.toArray(new IBinding[bindings.size()]);
}
private static void findBindingsForQualifiedName(IScope scope, String qualifiedName,
Collection<IBinding> bindings) {
private static void findBindingsForQualifiedName(IScope scope, String qualifiedName, Collection<IBinding> bindings,
IASTNode beforeNode) {
// Split the qualified name into the first part (before the first :: qualifier) and the rest. All
// bindings for the first part are found and their scope is used to find the rest of the name. When
// the call tree gets to a leaf (non-qualified name) then a simple lookup happens and all matching
@ -4318,14 +4340,20 @@ public class CPPSemantics {
// When we're down to a single component name, then use the normal lookup method.
if (part2 == null || part2.isEmpty()) {
bindings.addAll(Arrays.asList(findBindings(scope, part1, false)));
bindings.addAll(Arrays.asList(findBindings(scope, part1.toCharArray(), false, beforeNode)));
return;
}
// Find all bindings that match the first part of the name. For each such binding,
// lookup the second part of the name.
for (IBinding binding : findBindings(scope, part1, false)) {
findBindingsForQualifiedName(getLookupScope(binding), part2, bindings);
for (IBinding binding : findBindings(scope, part1.toCharArray(), false, beforeNode)) {
IScope lookupScope;
if (binding instanceof IScope) {
lookupScope = (IScope) binding;
} else {
lookupScope = getLookupScope(binding);
}
findBindingsForQualifiedName(lookupScope, part2, bindings, beforeNode);
}
}

View file

@ -32,6 +32,7 @@ import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldDesignator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
@ -108,6 +109,8 @@ public final class CPPVariableReadWriteFlags extends VariableReadWriteFlags {
return rwAssignmentToType(type, indirection);
}
}
} else if (grand instanceof ICPPASTStructuredBindingDeclaration) {
return rwInStructuredBinding((ICPPASTStructuredBindingDeclaration) grand);
}
return READ | WRITE; // fallback
}

View file

@ -31,7 +31,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
@ -90,6 +94,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.ILabel;
@ -141,6 +146,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
@ -155,6 +161,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
@ -224,6 +231,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPTemplateNonTypeArgument;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPTemplateParameterMap;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPTemplateTypeArgument;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPTypedef;
@ -231,6 +239,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPUnaryTypeTransformation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPUnknownTypeScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVariable;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVariableTemplate;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
@ -249,6 +258,10 @@ public class CPPVisitor extends ASTQueries {
private static final char[] TYPE_INFO = "type_info".toCharArray(); //$NON-NLS-1$
private static final char[] INITIALIZER_LIST = "initializer_list".toCharArray(); //$NON-NLS-1$
private static final char[][] EMPTY_CHAR_ARRAY_ARRAY = {};
private static final String STD_TUPLE_SIZE_STR = STD + "::tuple_size"; //$NON-NLS-1$
private static final String STD_TUPLE_ELEMENT_STR = STD + "::tuple_element"; //$NON-NLS-1$
private static final String VALUE_STR = "value"; //$NON-NLS-1$
private static final String TYPE_STR = "type"; //$NON-NLS-1$
public static final IASTInitializerClause[] NO_ARGS = {};
// Flags for createType().
@ -273,6 +286,19 @@ public class CPPVisitor extends ASTQueries {
}
};
public static final Predicate<IVariable> IS_STATIC_VARIABLE = IVariable::isStatic;
private static final Predicate<IVariable> HAS_INTEGRAL_TYPE = variable -> BuiltinOperators
.isIntegral(SemanticUtil.getUltimateTypeUptoPointers(variable.getType()));
private static final Predicate<IVariable> HAS_CONST_TYPE = field -> ExpressionTypes.isConst(field.getType());
private static final Predicate<IBinding> IS_NAMED_VALUE = binding -> binding.getName().equals(VALUE_STR);
/**
* Required until Java 9 Optional.stream()
*/
private static final <E> Stream<E> toStream(Optional<E> optional) {
return optional.map(Stream::of).orElse(Stream.empty());
}
public static IBinding createBinding(IASTName name) {
IASTNode parent = name.getParent();
IBinding binding = null;
@ -326,6 +352,8 @@ public class CPPVisitor extends ASTQueries {
return createBinding((IASTDeclarator) parent);
} else if (parent instanceof ICPPASTElaboratedTypeSpecifier) {
return createBinding((ICPPASTElaboratedTypeSpecifier) parent);
} else if (parent instanceof ICPPASTStructuredBindingDeclaration) {
return new CPPVariable(name);
} else if (parent instanceof IASTDeclaration) {
return createBinding((IASTDeclaration) parent);
} else if (parent instanceof ICPPASTEnumerationSpecifier) {
@ -1641,7 +1669,8 @@ public class CPPVisitor extends ASTQueries {
case KIND_OBJ_FN:
if (prop == IASTDeclarator.DECLARATOR_NAME
|| prop == IASTEnumerationSpecifier.IASTEnumerator.ENUMERATOR_NAME
|| prop == ICPPASTUsingDeclaration.NAME) {
|| prop == ICPPASTUsingDeclaration.NAME
|| prop == ICPPASTStructuredBindingDeclaration.IDENTIFIER) {
break;
}
return PROCESS_CONTINUE;
@ -1936,6 +1965,146 @@ public class CPPVisitor extends ASTQueries {
return pt;
}
private static boolean hasFieldNamedValue(ICPPClassSpecialization instance) {
return Arrays.stream(instance.getFields()).anyMatch(field -> field.getName().equals(VALUE_STR));
}
public static Optional<ICPPClassSpecialization> findTupleSizeWithValueMember(IType type, IScope scope,
IASTNode beforeNode) {
ICPPTemplateArgument[] templateArguments = new ICPPTemplateArgument[] { new CPPTemplateTypeArgument(type) };
return findClassTemplateInstance(scope, STD_TUPLE_SIZE_STR, templateArguments, beforeNode)
.filter(CPPVisitor::hasFieldNamedValue);
}
private static Optional<ICPPClassSpecialization> findClassTemplateInstance(IScope scope, String templateName,
ICPPTemplateArgument[] templateArguments, IASTNode point) {
IBinding[] tupleSizeBindings = CPPSemantics.findBindingsForQualifiedName(scope, templateName, point);
return Arrays.stream(tupleSizeBindings).filter(ICPPClassTemplate.class::isInstance)
.map(ICPPClassTemplate.class::cast)
.map(template -> CPPTemplates.instantiate(template, templateArguments))
.filter(ICPPClassSpecialization.class::isInstance).map(ICPPClassSpecialization.class::cast)
.filter(Objects::nonNull).findFirst();
}
private static IType determineTupleElementType(IType type, IScope scope, IASTName name, int index) {
ICPPTemplateArgument indexArgument = new CPPTemplateNonTypeArgument(IntegralValue.create(index),
CPPVisitor.get_SIZE_T());
ICPPTemplateArgument[] templateArguments = new ICPPTemplateArgument[] { indexArgument,
new CPPTemplateTypeArgument(type) };
return toStream(findClassTemplateInstance(scope, STD_TUPLE_ELEMENT_STR, templateArguments, name))
.map(ICPPClassSpecialization::getCompositeScope)
// Look for "type" in class scope with CPPSemantics.findBindings to also consider base classes
.map(instanceScope -> CPPSemantics.findBindings(instanceScope, TYPE_STR.toCharArray(), false, name))
.flatMap(Arrays::stream).filter(IType.class::isInstance).map(IType.class::cast)
.map(SemanticUtil::getSimplifiedType).findFirst()
.orElse(ProblemType.CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE);
}
private static ICPPASTInitializerClause getInitializerClause(ICPPASTStructuredBindingDeclaration declaration) {
IASTInitializer initializer = declaration.getInitializer();
ICPPASTInitializerClause initClause = null;
if (initializer != null) {
initClause = getInitClauseForInitializer(initializer);
} else {
IASTNode declarationParent = declaration.getParent();
if (declarationParent instanceof ICPPASTRangeBasedForStatement) {
ICPPASTRangeBasedForStatement rangeBasedForParent = (ICPPASTRangeBasedForStatement) declarationParent;
initClause = getAutoInitClauseForRangeBasedFor(rangeBasedForParent);
}
}
return initClause;
}
public static IType createType(ICPPASTStructuredBindingDeclaration declaration, IASTName name) {
ICPPASTInitializerClause initClause = getInitializerClause(declaration);
if (initClause == null) {
return ProblemType.CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE;
}
ICPPEvaluation evaluation = initClause.getEvaluation();
IType eType = evaluation.getType();
IType unwrappedType = SemanticUtil.getNestedType(eType, TDEF | ALLCVQ);
IType initializerType = deduceInitializerType(declaration, name, unwrappedType);
if (initializerType == ProblemType.CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE) {
return initializerType;
}
IASTDeclSpecifier declSpec = declaration.getDeclSpecifier();
IType qualifiedType = SemanticUtil.addQualifiers(initializerType,
declSpec.isConst() || SemanticUtil.isConst(eType),
declSpec.isVolatile() || SemanticUtil.isVolatile(eType), declSpec.isRestrict());
RefQualifier refQualifier = declaration.getRefQualifier();
if (refQualifier == null) {
return qualifiedType;
}
return new CPPReferenceType(qualifiedType, shallBecomeRvalueReference(evaluation, refQualifier, declSpec));
}
public static boolean hasConstexprStaticIntegralValueField(ICPPClassType type, long expectedValue) {
return Arrays.stream(ClassTypeHelper.getFields(type)).filter(IS_STATIC_VARIABLE).filter(IS_NAMED_VALUE)
.filter(HAS_INTEGRAL_TYPE).filter(HAS_CONST_TYPE).filter(field -> {
return variableHasInitialNumberValue(field, expectedValue);
}).findFirst().isPresent();
}
private static boolean variableHasInitialNumberValue(IVariable variable, long expectedValue) {
IValue initialValue = variable.getInitialValue();
if (initialValue == null) {
return false;
}
Number numberValue = initialValue.numberValue();
if (numberValue == null) {
return false;
}
return numberValue.longValue() == expectedValue;
}
private static IType deduceInitializerType(ICPPASTStructuredBindingDeclaration declaration, IASTName name,
IType unwrappedType) {
if (unwrappedType instanceof IArrayType) {
IArrayType arrayType = (IArrayType) unwrappedType;
return arrayType.getType();
} else if (unwrappedType instanceof ICPPClassType) {
int index = Arrays.asList(declaration.getNames()).indexOf(name);
IScope scope = CPPSemantics.getLookupScope(name);
Optional<ICPPClassSpecialization> tupleSizeInstance = findTupleSizeWithValueMember(unwrappedType, scope,
name);
if (tupleSizeInstance.isPresent()) {
int numberOfNames = declaration.getNames().length;
if (hasConstexprStaticIntegralValueField(tupleSizeInstance.get(), numberOfNames)) {
return determineTupleElementType(unwrappedType, scope, name, index);
}
} else {
ICPPClassType classType = (ICPPClassType) unwrappedType;
return Arrays.stream(ClassTypeHelper.getFields(classType)).filter(IS_STATIC_VARIABLE.negate())
.skip(index).findFirst().map(IField::getType)
.orElse(ProblemType.CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE);
}
}
return ProblemType.CANNOT_DEDUCE_STRUCTURED_BINDING_TYPE;
}
/**
*
* Determines the reference type of a name in a structured binding based on its {@code evaluation}, the {@code refQualifier} and the {@code declSpecifier}.
* It performs perfect forwarding in case of a forwarding reference, i.e. if the declaration specifier is exactly "auto &&",
* Otherwise it is always an rvalue reference if the declaration specifier contains &&
*
* Example:
* auto && [element] = get(); // element is an rvalue if get() is an rvalue, otherwise an lvalue
* auto const && [element] = get(); //element is always an rvalue, get() needs to be an rvalue too
*
*/
private static boolean shallBecomeRvalueReference(ICPPEvaluation evaluation, RefQualifier refQualifier,
IASTDeclSpecifier declSpecifier) {
if (refQualifier == RefQualifier.RVALUE) {
return evaluation.getValueCategory() != ValueCategory.LVALUE || declSpecifier.isConst()
|| declSpecifier.isVolatile();
}
return false;
}
private static IType createFunctionType(IType returnType, ICPPASTFunctionDeclarator fnDtor) {
IType[] pTypes = createParameterTypes(fnDtor);
@ -2214,8 +2383,7 @@ public class CPPVisitor extends ASTQueries {
return ProblemType.CANNOT_DEDUCE_AUTO_TYPE;
}
private static ICPPASTInitializerClause getAutoInitClauseForDeclarator(IASTDeclarator declarator) {
IASTInitializer initClause = declarator.getInitializer();
private static ICPPASTInitializerClause getInitClauseForInitializer(IASTInitializer initClause) {
if (initClause instanceof IASTEqualsInitializer) {
return (ICPPASTInitializerClause) ((IASTEqualsInitializer) initClause).getInitializerClause();
} else if (initClause instanceof ICPPASTConstructorInitializer) {
@ -2235,6 +2403,45 @@ public class CPPVisitor extends ASTQueries {
return null;
}
private static ICPPASTInitializerClause getAutoInitClauseForRangeBasedFor(ICPPASTRangeBasedForStatement forStmt) {
// See 6.5.4 The range-based for statement [stmt.ranged]
IASTInitializerClause forInit = forStmt.getInitializerClause();
IASTExpression beginExpr = null;
if (forInit instanceof IASTExpression) {
final IASTExpression expr = (IASTExpression) forInit;
IType type = SemanticUtil.getNestedType(expr.getExpressionType(), TDEF | CVTYPE);
if (type instanceof IArrayType) {
beginExpr = expr.copy();
}
}
if (beginExpr == null) {
IASTImplicitName[] implicits = forStmt.getImplicitNames();
if (implicits.length > 0) {
IBinding b = implicits[0].getBinding();
CPPASTName name = new CPPASTName();
name.setBinding(b);
IASTInitializerClause[] beginCallArguments = new IASTInitializerClause[] { forInit.copy() };
if (b instanceof ICPPMethod && forInit instanceof IASTExpression) {
beginExpr = new CPPASTFunctionCallExpression(
new CPPASTFieldReference(name, (IASTExpression) forInit.copy()), beginCallArguments);
} else {
beginExpr = new CPPASTFunctionCallExpression(new CPPASTIdExpression(name), beginCallArguments);
}
} else {
return null;
}
}
ICPPASTInitializerClause autoInitClause = new CPPASTUnaryExpression(IASTUnaryExpression.op_star, beginExpr);
autoInitClause.setParent(forStmt);
autoInitClause.setPropertyInParent(ICPPASTRangeBasedForStatement.INITIALIZER);
return autoInitClause;
}
private static ICPPASTInitializerClause getAutoInitClauseForDeclarator(IASTDeclarator declarator) {
IASTInitializer initClause = declarator.getInitializer();
return getInitClauseForInitializer(initClause);
}
private static IType createAutoType(final IASTDeclSpecifier declSpec, IASTDeclarator declarator, int flags,
PlaceholderKind placeholderKind) {
IType cannotDeduce = placeholderKind == PlaceholderKind.Auto ? ProblemType.CANNOT_DEDUCE_AUTO_TYPE
@ -2275,39 +2482,10 @@ public class CPPVisitor extends ASTQueries {
}
}
} else if (parent instanceof ICPPASTRangeBasedForStatement) {
// See 6.5.4 The range-based for statement [stmt.ranged]
ICPPASTRangeBasedForStatement forStmt = (ICPPASTRangeBasedForStatement) parent;
IASTInitializerClause forInit = forStmt.getInitializerClause();
IASTExpression beginExpr = null;
if (forInit instanceof IASTExpression) {
final IASTExpression expr = (IASTExpression) forInit;
IType type = SemanticUtil.getNestedType(expr.getExpressionType(), TDEF | CVTYPE);
if (type instanceof IArrayType) {
beginExpr = expr.copy();
}
autoInitClause = getAutoInitClauseForRangeBasedFor((ICPPASTRangeBasedForStatement) parent);
if (autoInitClause == null) {
return cannotDeduce;
}
if (beginExpr == null) {
IASTImplicitName[] implicits = forStmt.getImplicitNames();
if (implicits.length > 0) {
IBinding b = implicits[0].getBinding();
CPPASTName name = new CPPASTName();
name.setBinding(b);
IASTInitializerClause[] beginCallArguments = new IASTInitializerClause[] { forInit.copy() };
if (b instanceof ICPPMethod && forInit instanceof IASTExpression) {
beginExpr = new CPPASTFunctionCallExpression(
new CPPASTFieldReference(name, (IASTExpression) forInit.copy()),
beginCallArguments);
} else {
beginExpr = new CPPASTFunctionCallExpression(new CPPASTIdExpression(name),
beginCallArguments);
}
} else {
return cannotDeduce;
}
}
autoInitClause = new CPPASTUnaryExpression(IASTUnaryExpression.op_star, beginExpr);
autoInitClause.setParent(forStmt);
autoInitClause.setPropertyInParent(ICPPASTRangeBasedForStatement.INITIALIZER);
} else if (parent instanceof IASTCompositeTypeSpecifier
&& declSpec.getStorageClass() != IASTDeclSpecifier.sc_static) {
// Non-static auto-typed class members are not allowed.

View file

@ -35,6 +35,10 @@ public class ExecIncomplete implements ICPPExecution {
@Override
public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException {
throw new UnsupportedOperationException();
buffer.putShort(ITypeMarshalBuffer.EXEC_INCOMPLETE);
}
public static ICPPExecution unmarshal(short firstBytes, ITypeMarshalBuffer buffer) {
return INSTANCE;
}
}

View file

@ -122,7 +122,7 @@ public class ExpressionTypes {
return false;
}
private static boolean isVolatile(IType type) {
public static boolean isVolatile(IType type) {
if (type instanceof IQualifierType) {
return ((IQualifierType) type).isVolatile();
} else if (type instanceof IPointerType) {

View file

@ -908,11 +908,22 @@ public class SemanticUtil {
if (type instanceof ICPPReferenceType) {
ICPPReferenceType refType = (ICPPReferenceType) type;
return isConst(refType.getType());
} else if (type instanceof CPPQualifierType) {
CPPQualifierType qualifierType = (CPPQualifierType) type;
return qualifierType.isConst();
}
return false;
return ExpressionTypes.isConst(type);
}
/**
* Returns whether a type is volatile or a reference to a volatile type.
*
* @param type the type to be checked
* @return true if the type is volatile, otherwise false
*/
public static boolean isVolatile(IType type) {
if (type instanceof ICPPReferenceType) {
ICPPReferenceType refType = (ICPPReferenceType) type;
return isVolatile(refType.getType());
}
return ExpressionTypes.isVolatile(type);
}
static IType[] addImplicitParameterType(IType[] types, ICPPMethod m) {

View file

@ -25,6 +25,7 @@ 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.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
@ -34,6 +35,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification;
@ -41,6 +43,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStaticAssertDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization;
@ -63,6 +66,8 @@ import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
public class DeclarationWriter extends NodeWriter {
private static final char OPEN_PAREN = '(';
private static final char CLOSE_PAREN = ')';
private static final char OPEN_BRACKET = '[';
private static final char CLOSE_BRACKET = ']';
private static final String ASM_START = "asm" + OPEN_PAREN; //$NON-NLS-1$
private static final String TEMPLATE_DECLARATION = "template<"; //$NON-NLS-1$
private static final String TEMPLATE_SPECIALIZATION = "template<> "; //$NON-NLS-1$
@ -87,6 +92,8 @@ public class DeclarationWriter extends NodeWriter {
addNewLine = false;
} else if (declaration instanceof IASTProblemDeclaration) {
throw new ProblemRuntimeException((IASTProblemDeclaration) declaration);
} else if (declaration instanceof ICPPASTStructuredBindingDeclaration) {
writeStructuredBinding((ICPPASTStructuredBindingDeclaration) declaration, writeSemicolon);
} else if (declaration instanceof IASTSimpleDeclaration) {
writeSimpleDeclaration((IASTSimpleDeclaration) declaration);
} else if (declaration instanceof ICPPASTExplicitTemplateInstantiation) {
@ -123,6 +130,28 @@ public class DeclarationWriter extends NodeWriter {
}
}
private void writeStructuredBinding(ICPPASTStructuredBindingDeclaration declaration, boolean writeSemicolon) {
writeAttributes(declaration, EnumSet.of(SpaceLocation.AFTER));
declaration.getDeclSpecifier().accept(visitor);
RefQualifier refQualifier = declaration.getRefQualifier();
if (refQualifier != null) {
writeRefQualifier(refQualifier);
}
scribe.printSpace();
scribe.print(OPEN_BRACKET);
writeNodeList(declaration.getNames());
scribe.print(CLOSE_BRACKET);
IASTInitializer initializer = declaration.getInitializer();
if (initializer != null) {
initializer.accept(visitor);
}
if (writeSemicolon) {
printSemicolon();
}
}
private void writeAliasDeclaration(ICPPASTAliasDeclaration aliasDeclaration) {
scribe.printStringSpace(Keywords.USING);
IASTName alias = aliasDeclaration.getAlias();

View file

@ -159,16 +159,7 @@ public class DeclaratorWriter extends NodeWriter {
scribe.print(Keywords.VOLATILE);
}
RefQualifier refQualifier = funcDec.getRefQualifier();
if (refQualifier != null) {
switch (refQualifier) {
case LVALUE:
scribe.print(Keywords.cpAMPER);
break;
case RVALUE:
scribe.print(Keywords.cpAND);
break;
}
}
writeRefQualifier(refQualifier);
if (funcDec.isMutable()) {
scribe.printSpace();
scribe.print(Keywords.MUTABLE);

View file

@ -23,8 +23,10 @@ import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
import org.eclipse.cdt.core.dom.ast.IASTAttributeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.gnu.IGCCASTAttributeList;
import org.eclipse.cdt.core.dom.parser.cpp.ICPPASTAttributeSpecifier;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
@ -157,4 +159,17 @@ public class NodeWriter {
scribe.printSpace();
}
}
protected void writeRefQualifier(RefQualifier refQualifier) {
if (refQualifier != null) {
switch (refQualifier) {
case LVALUE:
scribe.print(Keywords.cpAMPER);
break;
case RVALUE:
scribe.print(Keywords.cpAND);
break;
}
}
}
}

View file

@ -154,6 +154,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecDo;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecExpressionStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecFor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecIf;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecIncomplete;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecRangeBasedFor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecReturn;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecSimpleDeclaration;
@ -1768,6 +1769,8 @@ class PDOMCPPLinkage extends PDOMLinkage implements IIndexCPPBindingConstants {
return ExecSwitch.unmarshal(firstBytes, buffer);
case ITypeMarshalBuffer.EXEC_CONSTRUCTOR_CHAIN:
return ExecConstructorChain.unmarshal(firstBytes, buffer);
case ITypeMarshalBuffer.EXEC_INCOMPLETE:
return ExecIncomplete.unmarshal(firstBytes, buffer);
}
throw new CoreException(CCorePlugin.createStatus("Cannot unmarshal an execution, first bytes=" + firstBytes)); //$NON-NLS-1$
}

View file

@ -2468,6 +2468,84 @@ public class DefaultCodeFormatterConstants {
*/
public static final String FORMATTER_INSERT_SPACE_BETWEEN_EMPTY_PARENS_IN_EXCEPTION_SPECIFICATION = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_between_empty_parens_in_exception_specification"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space before the opening bracket of the name list in a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_before_opening_structured_binding_name_list"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_before_opening_structured_binding_name_list"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space after the opening bracket of the name list in a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_after_opening_structured_binding_name_list"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: DO_NOT_INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_after_opening_structured_binding_name_list"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space before the closing bracket of the name list in a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_before_closing_structured_binding_name_list"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: DO_NOT_INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_before_closing_structured_binding_name_list"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space before a comma in the name list of a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_before_comma_in_structured_binding_name_list"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: DO_NOT_INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_before_comma_in_structured_binding_name_list"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space after a comma in the name list of a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_after_comma_in_structured_binding_name_list"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_after_comma_in_structured_binding_name_list"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to insert a space before a reference qualifier in a structured binding declaration
* - option id: "org.eclipse.cdt.core.formatter.insert_space_before_ref_qualifier_in_structured_binding"
* - possible values: { INSERT, DO_NOT_INSERT }
* - default: INSERT
* </pre>
* @see CCorePlugin#INSERT
* @see CCorePlugin#DO_NOT_INSERT
* @since 6.9
*/
public static final String FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING = CCorePlugin.PLUGIN_ID
+ ".formatter.insert_space_before_ref_qualifier_in_structured_binding"; //$NON-NLS-1$
/**
* <pre>
* FORMATTER / Option to keep else statement on the same line

View file

@ -301,6 +301,18 @@ public class DefaultCodeFormatterOptions {
public boolean insert_space_between_empty_parens_in_method_declaration;
public boolean insert_space_between_empty_parens_in_method_invocation;
public boolean insert_space_between_empty_parens_in_exception_specification;
/** @since 6.9 */
public boolean insert_space_before_opening_structured_binding_name_list;
/** @since 6.9 */
public boolean insert_space_after_opening_structured_binding_name_list;
/** @since 6.9 */
public boolean insert_space_before_closing_structured_binding_name_list;
/** @since 6.9 */
public boolean insert_space_before_comma_in_structured_binding_name_list;
/** @since 6.9 */
public boolean insert_space_after_comma_in_structured_binding_name_list;
/** @since 6.9 */
public boolean insert_space_before_ref_qualifier_in_structured_binding;
public boolean compact_else_if;
public boolean keep_guardian_clause_on_one_line;
public boolean keep_else_statement_on_same_line;
@ -788,6 +800,24 @@ public class DefaultCodeFormatterOptions {
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BETWEEN_EMPTY_PARENS_IN_EXCEPTION_SPECIFICATION,
this.insert_space_between_empty_parens_in_exception_specification ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST,
this.insert_space_before_opening_structured_binding_name_list ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST,
this.insert_space_after_opening_structured_binding_name_list ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST,
this.insert_space_before_closing_structured_binding_name_list ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
this.insert_space_before_comma_in_structured_binding_name_list ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
this.insert_space_after_comma_in_structured_binding_name_list ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING,
this.insert_space_before_ref_qualifier_in_structured_binding ? CCorePlugin.INSERT
: CCorePlugin.DO_NOT_INSERT);
options.put(DefaultCodeFormatterConstants.FORMATTER_COMPACT_ELSE_IF,
this.compact_else_if ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE);
options.put(DefaultCodeFormatterConstants.FORMATTER_KEEP_GUARDIAN_CLAUSE_ON_ONE_LINE,
@ -2130,6 +2160,42 @@ public class DefaultCodeFormatterOptions {
this.insert_space_between_empty_parens_in_exception_specification = CCorePlugin.INSERT
.equals(insertSpaceBetweenEmptyParensInExceptionSpecificationOption);
}
final Object insertSpaceBeforeOpeningStructuredBindingNameList = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST);
if (insertSpaceBeforeOpeningStructuredBindingNameList != null) {
this.insert_space_before_opening_structured_binding_name_list = CCorePlugin.INSERT
.equals(insertSpaceBeforeOpeningStructuredBindingNameList);
}
final Object insertSpaceAfterOpeningStructuredBindingNameList = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST);
if (insertSpaceAfterOpeningStructuredBindingNameList != null) {
this.insert_space_after_opening_structured_binding_name_list = CCorePlugin.INSERT
.equals(insertSpaceAfterOpeningStructuredBindingNameList);
}
final Object insertSpaceBeforeClosingStructuredBindingNameList = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST);
if (insertSpaceBeforeClosingStructuredBindingNameList != null) {
this.insert_space_before_closing_structured_binding_name_list = CCorePlugin.INSERT
.equals(insertSpaceBeforeClosingStructuredBindingNameList);
}
final Object insertSpaceBeforeCommaInStructuredBindingNameList = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST);
if (insertSpaceBeforeCommaInStructuredBindingNameList != null) {
this.insert_space_before_comma_in_structured_binding_name_list = CCorePlugin.INSERT
.equals(insertSpaceBeforeCommaInStructuredBindingNameList);
}
final Object insertSpaceAfterCommaInStructuredBindingNameList = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST);
if (insertSpaceAfterCommaInStructuredBindingNameList != null) {
this.insert_space_after_comma_in_structured_binding_name_list = CCorePlugin.INSERT
.equals(insertSpaceAfterCommaInStructuredBindingNameList);
}
final Object insertSpaceBeforeRefQualifierInStructuredBinding = settings
.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING);
if (insertSpaceBeforeRefQualifierInStructuredBinding != null) {
this.insert_space_before_ref_qualifier_in_structured_binding = CCorePlugin.INSERT
.equals(insertSpaceBeforeRefQualifierInStructuredBinding);
}
final Object compactElseIfOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_COMPACT_ELSE_IF);
if (compactElseIfOption != null) {
this.compact_else_if = DefaultCodeFormatterConstants.TRUE.equals(compactElseIfOption);
@ -2441,6 +2507,12 @@ public class DefaultCodeFormatterOptions {
this.insert_space_between_empty_parens_in_method_declaration = false;
this.insert_space_between_empty_parens_in_method_invocation = false;
this.insert_space_between_empty_parens_in_exception_specification = false;
this.insert_space_before_opening_structured_binding_name_list = true;
this.insert_space_after_opening_structured_binding_name_list = false;
this.insert_space_before_closing_structured_binding_name_list = false;
this.insert_space_before_comma_in_structured_binding_name_list = false;
this.insert_space_after_comma_in_structured_binding_name_list = true;
this.insert_space_before_ref_qualifier_in_structured_binding = false;
this.compact_else_if = true;
this.keep_guardian_clause_on_one_line = false;
this.keep_else_statement_on_same_line = false;

View file

@ -131,6 +131,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator.RefQualifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTIfStatement;
@ -149,6 +150,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStructuredBindingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
@ -1948,7 +1950,48 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
return PROCESS_SKIP;
}
private int visit(ICPPASTStructuredBindingDeclaration node) {
formatLeadingAttributes(node);
IASTDeclSpecifier declSpec = node.getDeclSpecifier();
declSpec.accept(this);
RefQualifier refQualifier = node.getRefQualifier();
if (refQualifier != null) {
int expectedToken = refQualifier == RefQualifier.LVALUE ? Token.tAMPER : Token.tAND;
if (peekNextToken() == expectedToken) {
scribe.printNextToken(expectedToken,
preferences.insert_space_before_ref_qualifier_in_structured_binding);
}
}
IASTInitializer initializer = node.getInitializer();
if (peekNextToken() == Token.tLBRACKET) {
List<IASTName> names = Arrays.asList(node.getNames());
final ListOptions options = new ListOptions(preferences.alignment_for_declarator_list);
options.leftToken = Token.tLBRACKET;
options.rightToken = Token.tRBRACKET;
options.fSpaceBeforeOpeningParen = preferences.insert_space_before_opening_structured_binding_name_list;
options.fSpaceAfterOpeningParen = preferences.insert_space_after_opening_structured_binding_name_list;
options.fSpaceBeforeClosingParen = preferences.insert_space_before_closing_structured_binding_name_list;
options.fSpaceAfterSeparator = preferences.insert_space_after_comma_in_structured_binding_name_list;
options.fSpaceBeforeSeparator = preferences.insert_space_before_comma_in_structured_binding_name_list;
formatList(names, options, true, false, null);
} else if (initializer != null) {
skipToNode(initializer);
}
if (initializer != null) {
initializer.accept(this);
}
if (fExpectSemicolonAfterDeclaration && peekNextToken() == Token.tSEMI) {
scribe.printNextToken(Token.tSEMI);
}
return PROCESS_SKIP;
}
private int visit(IASTSimpleDeclaration node) {
if (node instanceof ICPPASTStructuredBindingDeclaration) {
return visit((ICPPASTStructuredBindingDeclaration) node);
}
formatLeadingAttributes(node);
IASTDeclSpecifier declSpec = node.getDeclSpecifier();
declSpec.accept(this);
@ -2585,11 +2628,12 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private void formatInlineDeclaration(final IASTDeclaration decl) {
boolean previousExpectSemicolonAfterDeclaration = fExpectSemicolonAfterDeclaration;
fExpectSemicolonAfterDeclaration = false;
try {
decl.accept(this);
} finally {
fExpectSemicolonAfterDeclaration = true;
fExpectSemicolonAfterDeclaration = previousExpectSemicolonAfterDeclaration;
}
}
@ -3660,7 +3704,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
scribe.space();
}
IASTDeclaration declaration = node.getDeclaration();
declaration.accept(this);
formatInlineDeclaration(declaration);
scribe.printNextToken(Token.tCOLON, true /* preferences.insert_space_before_colon_in_for */);
final IASTInitializerClause initializer = node.getInitializerClause();
if (true /*preferences.insert_space_after_colon_in_for*/) {

View file

@ -133,4 +133,15 @@ void functionWithLabelReferenceGoto() {
goto *labelPointer;
referencedLabel:
return;
}
void localOccurrencesInStructuredBinding() {
int decompArr[2]{1, 2};
auto [decomposedF, decomposedS] = decompArr;
decomposedF;
decomposedS;
auto [decomposedF2, decomposedS2](decompArr);
auto [decomposedF3, decomposedS3]{decompArr};
auto & [decomposedF4, decomposedS4] = decompArr;
auto const & [decomposedF5,decomposedS5] = decompArr;
}

View file

@ -4517,4 +4517,186 @@ public class CodeFormatterTest extends BaseUITestCase {
public void testWrappingLambdaExpression_Bug549653() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto[l,r]=s;
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto [l, r] = s;
//}
public void testStructuredBindingSimpleDeclaration() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto&[l0,r0]=s;
// auto&&[l1,r1]=S{1,1.0};
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto& [l0, r0] = s;
// auto&& [l1, r1] = S { 1, 1.0 };
//}
public void testStructuredBindingWithRefQualifiers() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto[l0,r0]=s;
// auto&[l1,r1]=s;
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// auto[ l0 ,r0 ] = s;
// auto &[ l1 ,r1 ] = s;
//}
public void testStructuredBindingSimpleDeclarationInvertedFormat() throws Exception {
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING,
CCorePlugin.INSERT);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST,
CCorePlugin.DO_NOT_INSERT);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST,
CCorePlugin.INSERT);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST,
CCorePlugin.INSERT);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
CCorePlugin.INSERT);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
CCorePlugin.DO_NOT_INSERT);
assertFormatterResult();
}
//#define SB(F, S, I) auto[F,S] = I
//#define LIST(F, S) [F,S]
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// SB(l0,r0,s);
// auto LIST(l1,r1)=s;
//}
//#define SB(F, S, I) auto[F,S] = I
//#define LIST(F, S) [F,S]
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// SB(l0, r0, s);
// auto LIST(l1,r1) = s;
//}
public void testStructuredBindingSimpleDeclarationFromMacro() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} esses[] { { 1, 1.0 } };
//void foo() {
// for (auto[l,r]: esses) {
// }
//}
//struct S {
// int i;
// double d;
//} esses[] { { 1, 1.0 } };
//void foo() {
// for (auto [l, r] : esses) {
// }
//}
public void testStructuredBindingInRangeBasedForLoop() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// for(auto[l,r]=s;l<10;l++){
// }
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// for (auto [l, r] = s; l < 10; l++) {
// }
//}
public void testStructuredBindingInForLoop() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// if(auto[l,r]=s;l==1){
// }
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// if (auto [l, r] = s; l == 1) {
// }
//}
public void testStructuredBindingInIfInitStatement() throws Exception {
assertFormatterResult();
}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// switch(auto[l,r]=s;l){
// }
//}
//struct S {
// int i;
// double d;
//} s { 1, 1.0 };
//void foo() {
// switch (auto [l, r] = s; l) {
// }
//}
public void testStructuredBindingInSwitchInitStatement() throws Exception {
assertFormatterResult();
}
}

View file

@ -557,6 +557,32 @@ public class MarkOccurrenceTest extends BaseUITestCase {
assertOccurrencesInWidget();
}
public void testMarkReferencedStructuredBindingDefinition() {
try {
fMatch = fFindReplaceDocumentAdapter.find(0, "decomposedF", true, true, true, false);
} catch (BadLocationException e) {
fail();
}
fEditor.selectAndReveal(fMatch.getOffset(), fMatch.getLength());
assertOccurrences(2, 1);
assertOccurrencesInWidget();
}
public void testMarkReferencedStructuredBindingInitializer() {
try {
fMatch = fFindReplaceDocumentAdapter.find(0, "decompArr", true, true, true, false);
} catch (BadLocationException e) {
fail();
}
fEditor.selectAndReveal(fMatch.getOffset(), fMatch.getLength());
assertOccurrences(6, 2);
assertOccurrencesInWidget();
}
private void assertOccurrencesInWidget() {
EditorTestHelper.runEventQueue(100);

View file

@ -1380,4 +1380,38 @@ public class CPPSelectionTestsIndexer extends BaseSelectionTestsIndexer {
assertInstance(target, IASTName.class);
assertEquals(IASTNameOwner.r_definition, ((IASTName) target).getRoleOfName(false));
}
// template <typename E>
// struct Node {
// Node * next;
// E value;
// };
// Node<int> head{nullptr, 42};
// auto [h, v] = head;
// #include "SBTestHeader.hpp"
// auto myH = h;
// auto myV = v;
public void testNavigationToStructuredBinding_522200() throws Exception {
StringBuilder[] buffers = getContents(2);
String header = buffers[0].toString();
IFile headerFile = importFile("SBTestHeader.hpp", header);
String source = buffers[1].toString();
IFile sourceFile = importFile("SBTestSource.cpp", source);
waitUntilFileIsIndexed(index, sourceFile);
IASTNode targetH = testF3(sourceFile, source.indexOf("myH = h") + 6);
assertInstance(targetH, IASTName.class);
assertEquals(IASTNameOwner.r_definition, ((IASTName) targetH).getRoleOfName(false));
IASTFileLocation locationH = targetH.getFileLocation();
int targetHOffset = locationH.getNodeOffset();
assertEquals(header.indexOf("auto [h") + 6, targetHOffset);
IASTNode targetV = testF3(sourceFile, source.indexOf("myV = v") + 6);
assertInstance(targetV, IASTName.class);
assertEquals(IASTNameOwner.r_definition, ((IASTName) targetV).getRoleOfName(false));
IASTFileLocation locationV = targetV.getFileLocation();
int targetVOffset = locationV.getNodeOffset();
assertEquals(header.indexOf("[h, v") + 4, targetVOffset);
}
}

View file

@ -1418,4 +1418,33 @@ public class CPPSelectionTestsNoIndexer extends BaseSelectionTests {
assertInstance(target, IASTName.class);
assertEquals(IASTNameOwner.r_definition, ((IASTName) target).getRoleOfName(false));
}
//int arr[2]{1, 2};
//auto [e1, e2] = arr;
//auto r1 = e1;
//auto r2 = e2;
public void testOpenDeclarationForStructuredBinding_522200() throws Exception {
String code = getAboveComment();
IFile file = importFile("testSB_.cpp", code); //$NON-NLS-1$
int offsetE1Reference = code.indexOf("e1;"); //$NON-NLS-1$
IASTNode e1Declaration = testF3(file, offsetE1Reference);
assertTrue(e1Declaration instanceof IASTName);
String expectedE1Name = "e1"; //$NON-NLS-1$
assertEquals(expectedE1Name, ((IASTName) e1Declaration).toString());
assertEquals(code.indexOf(expectedE1Name), ((ASTNode) e1Declaration).getOffset());
assertEquals(expectedE1Name.length(), ((ASTNode) e1Declaration).getLength());
assertEquals(IASTNameOwner.r_definition, ((IASTName) e1Declaration).getRoleOfName(false));
int offsetE2Reference = code.indexOf("e2;"); //$NON-NLS-1$
IASTNode e2Declaration = testF3(file, offsetE2Reference);
assertTrue(e2Declaration instanceof IASTName);
String expectedE2Name = "e2"; //$NON-NLS-1$
assertEquals(expectedE2Name, ((IASTName) e2Declaration).toString());
assertEquals(code.indexOf(expectedE2Name), ((ASTNode) e2Declaration).getOffset());
assertEquals(expectedE2Name.length(), ((ASTNode) e2Declaration).getLength());
assertEquals(IASTNameOwner.r_definition, ((IASTName) e2Declaration).getRoleOfName(false));
}
}

View file

@ -78,6 +78,7 @@ final class FormatterMessages extends NLS {
public static String WhiteSpaceTabPage_if;
public static String WhiteSpaceTabPage_for;
public static String WhiteSpaceTabPage_labels;
public static String WhiteSpaceTabPage_structured_bindings;
public static String WhiteSpaceTabPage_template_arguments;
public static String WhiteSpaceTabPage_template_parameters;
public static String WhiteSpaceTabPage_conditionals;
@ -204,6 +205,13 @@ final class FormatterMessages extends NLS {
public static String WhiteSpaceOptions_after_semicolon;
public static String WhiteSpaceOptions_before_question_mark;
public static String WhiteSpaceOptions_after_question_mark;
public static String WhiteSpaceOptions_structured_binding_declarations;
public static String WhiteSpaceOptions_structured_binding_before_ref_qualifier;
public static String WhiteSpaceOptions_structured_binding_before_name_list_opening_bracket;
public static String WhiteSpaceOptions_structured_binding_before_first_name_in_list;
public static String WhiteSpaceOptions_structured_binding_before_comma_in_name_list;
public static String WhiteSpaceOptions_structured_binding_after_comma_in_name_list;
public static String WhiteSpaceOptions_structured_binding_before_name_list_closing_bracket;
// public static String WhiteSpaceOptions_before_ellipsis;
// public static String WhiteSpaceOptions_after_ellipsis;
// public static String WhiteSpaceOptions_return_with_parenthesized_expression;

View file

@ -79,6 +79,7 @@ WhiteSpaceTabPage_for='for'
#WhiteSpaceTabPage_for_after_comma_inc=after comma in increments
WhiteSpaceTabPage_labels=Labels
WhiteSpaceTabPage_structured_bindings=Structured bindings
WhiteSpaceTabPage_template_arguments=Template arguments
WhiteSpaceTabPage_template_parameters=Template parameters
@ -235,6 +236,14 @@ WhiteSpaceOptions_after_semicolon=After semicolon
WhiteSpaceOptions_before_question_mark=Before question mark
WhiteSpaceOptions_after_question_mark=After question mark
WhiteSpaceOptions_structured_binding_declarations=Structured binding declarations
WhiteSpaceOptions_structured_binding_before_ref_qualifier=Before reference qualifier
WhiteSpaceOptions_structured_binding_before_name_list_opening_bracket=Before opening bracket
WhiteSpaceOptions_structured_binding_before_name_list_closing_bracket=Before closing bracket
WhiteSpaceOptions_structured_binding_before_first_name_in_list=Before first name
WhiteSpaceOptions_structured_binding_before_comma_in_name_list=Before comma
WhiteSpaceOptions_structured_binding_after_comma_in_name_list=After comma
#WhiteSpaceOptions_before_ellipsis=Before Ellipsis
#WhiteSpaceOptions_after_ellipsis=After Ellipsis

View file

@ -236,6 +236,10 @@ public final class WhiteSpaceOptions {
"map<int,int> m;" //$NON-NLS-1$
);
private final PreviewSnippet STRUCTURED_BINDING_PREVIEW = new PreviewSnippet(CodeFormatter.K_STATEMENTS,
"auto & [first, second, third] = init;" //$NON-NLS-1$
);
/**
* Create the tree, in this order: syntax element - position - abstract element
* @param workingValues
@ -477,6 +481,7 @@ public final class WhiteSpaceOptions {
createMethodDeclTree(workingValues, declarations);
createExceptionSpecificationTree(workingValues, declarations);
createLabelTree(workingValues, declarations);
createStructuredBindingTree(workingValues, declarations);
final InnerNode statements = new InnerNode(null, workingValues, FormatterMessages.WhiteSpaceTabPage_statements);
createOption(statements, workingValues, FormatterMessages.WhiteSpaceOptions_before_semicolon,
@ -582,6 +587,10 @@ public final class WhiteSpaceOptions {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_exception_specification,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_METHOD_DECLARATION_THROWS,
METHOD_DECL_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private void createBeforeOperatorTree(Map<String, String> workingValues, final InnerNode parent) {
@ -602,6 +611,9 @@ public final class WhiteSpaceOptions {
private void createBeforeClosingBracketTree(Map<String, String> workingValues, final InnerNode parent) {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_arrays,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACKET, ARRAY_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private void createBeforeClosingAngleBracketTree(Map<String, String> workingValues, final InnerNode parent) {
@ -616,6 +628,9 @@ public final class WhiteSpaceOptions {
private void createBeforeOpenBracketTree(Map<String, String> workingValues, final InnerNode parent) {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_arrays,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACKET, ARRAY_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private void createBeforeOpenAngleBracketTree(Map<String, String> workingValues, final InnerNode parent) {
@ -703,6 +718,9 @@ public final class WhiteSpaceOptions {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_declarator_list,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_POINTER_IN_DECLARATOR_LIST,
DECLARATOR_LIST_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING,
STRUCTURED_BINDING_PREVIEW);
}
private void createAfterPointerTree(Map<String, String> workingValues, final InnerNode parent) {
@ -814,6 +832,10 @@ public final class WhiteSpaceOptions {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_exception_specification,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_DECLARATION_THROWS,
METHOD_DECL_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private void createAfterOperatorTree(Map<String, String> workingValues, final InnerNode parent) {
@ -835,6 +857,9 @@ public final class WhiteSpaceOptions {
private void createAfterOpenBracketTree(Map<String, String> workingValues, final InnerNode parent) {
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_arrays,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACKET, ARRAY_PREVIEW);
createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_declarations,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private void createAfterOpenAngleBracketTree(Map<String, String> workingValues, final InnerNode parent) {
@ -1268,6 +1293,33 @@ public final class WhiteSpaceOptions {
return root;
}
private void createStructuredBindingTree(Map<String, String> workingValues, InnerNode parent) {
InnerNode root = new InnerNode(parent, workingValues, FormatterMessages.WhiteSpaceTabPage_structured_bindings);
createOption(root, workingValues, FormatterMessages.WhiteSpaceOptions_structured_binding_before_ref_qualifier,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_REF_QUALIFIER_IN_STRUCTURED_BINDING,
STRUCTURED_BINDING_PREVIEW);
createOption(root, workingValues,
FormatterMessages.WhiteSpaceOptions_structured_binding_before_name_list_opening_bracket,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
createOption(root, workingValues,
FormatterMessages.WhiteSpaceOptions_structured_binding_before_name_list_closing_bracket,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
createOption(root, workingValues,
FormatterMessages.WhiteSpaceOptions_structured_binding_before_first_name_in_list,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
createOption(root, workingValues,
FormatterMessages.WhiteSpaceOptions_structured_binding_before_comma_in_name_list,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
createOption(root, workingValues,
FormatterMessages.WhiteSpaceOptions_structured_binding_after_comma_in_name_list,
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_STRUCTURED_BINDING_NAME_LIST,
STRUCTURED_BINDING_PREVIEW);
}
private InnerNode createTemplateArgumentTree(Map<String, String> workingValues, InnerNode parent) {
final InnerNode root = new InnerNode(parent, workingValues,
FormatterMessages.WhiteSpaceTabPage_template_arguments);