From acbceb04ee9e472119ef3255a47986c833fcf4c2 Mon Sep 17 00:00:00 2001 From: Thomas Corbat Date: Wed, 23 May 2018 13:02:03 +0200 Subject: 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 Signed-off-by: Thomas Corbat --- .../parser/tests/ast2/AST2CPPAttributeTests.java | 6 + .../cdt/core/parser/tests/ast2/CommonCPPTypes.java | 72 ++++ .../cdt/core/parser/tests/ast2/CommonCTypes.java | 42 ++ .../core/parser/tests/ast2/DOMParserTestSuite.java | 2 + .../core/parser/tests/ast2/SemanticTestBase.java | 75 +--- .../tests/ast2/cxx14/GenericLambdaIndexTests.java | 1 + .../tests/ast2/cxx14/GenericLambdaTests.java | 1 + .../parser/tests/ast2/cxx14/InitCaptureTests.java | 1 + .../ast2/cxx14/ReturnTypeDeductionIndexTests.java | 1 + .../tests/ast2/cxx14/ReturnTypeDeductionTests.java | 1 + .../tests/ast2/cxx14/VariableTemplateTests.java | 1 + .../cxx14/constexpr/AllConstexprEvalTests.java | 2 + .../cxx14/constexpr/StructuredBindingTests.java | 468 +++++++++++++++++++++ .../tests/ast2/cxx14/constexpr/TestBase.java | 6 + .../ast2/cxx17/StructuredBindingIndexTests.java | 295 +++++++++++++ .../tests/ast2/cxx17/StructuredBindingTests.java | 418 ++++++++++++++++++ .../cdt/internal/index/tests/IndexTests.java | 2 + .../rewrite/ASTWriterAttributeTestSource.awts | 7 +- .../rewrite/ASTWriterDeclarationTestSource.awts | 14 + .../rewrite/ASTWriterStatementTestSource.awts | 15 + core/org.eclipse.cdt.core/META-INF/MANIFEST.MF | 3 +- .../eclipse/cdt/core/dom/ast/ISemanticProblem.java | 2 + .../cpp/ICPPASTStructuredBindingDeclaration.java | 108 +++++ .../cdt/core/dom/ast/cpp/ICPPNodeFactory.java | 12 + .../dom/parser/AbstractGNUSourceCodeParser.java | 34 ++ .../core/dom/parser/ITypeMarshalBuffer.java | 2 +- .../cdt/internal/core/dom/parser/ProblemType.java | 2 + .../core/dom/parser/VariableReadWriteFlags.java | 15 + .../core/dom/parser/cpp/CPPASTImplicitName.java | 5 + .../dom/parser/cpp/CPPASTSimpleDeclaration.java | 6 +- .../cpp/CPPASTStructuredBindingDeclaration.java | 210 +++++++++ .../core/dom/parser/cpp/CPPImplicitVariable.java | 53 +++ .../core/dom/parser/cpp/CPPNodeFactory.java | 13 + .../parser/cpp/CPPStructuredBindingComposite.java | 239 +++++++++++ .../internal/core/dom/parser/cpp/CPPVariable.java | 59 ++- .../core/dom/parser/cpp/GNUCPPSourceParser.java | 78 +++- .../core/dom/parser/cpp/VariableHelpers.java | 4 + .../dom/parser/cpp/semantics/CPPSemantics.java | 46 +- .../cpp/semantics/CPPVariableReadWriteFlags.java | 3 + .../core/dom/parser/cpp/semantics/CPPVisitor.java | 248 +++++++++-- .../dom/parser/cpp/semantics/ExecIncomplete.java | 6 +- .../dom/parser/cpp/semantics/ExpressionTypes.java | 2 +- .../dom/parser/cpp/semantics/SemanticUtil.java | 19 +- .../dom/rewrite/astwriter/DeclarationWriter.java | 29 ++ .../dom/rewrite/astwriter/DeclaratorWriter.java | 11 +- .../core/dom/rewrite/astwriter/NodeWriter.java | 15 + .../internal/core/pdom/dom/cpp/PDOMCPPLinkage.java | 3 + .../formatter/DefaultCodeFormatterConstants.java | 78 ++++ .../formatter/DefaultCodeFormatterOptions.java | 72 ++++ .../internal/formatter/CodeFormatterVisitor.java | 48 ++- .../resources/ceditor/occurrences.cpp | 11 + .../cdt/ui/tests/text/CodeFormatterTest.java | 182 ++++++++ .../cdt/ui/tests/text/MarkOccurrenceTest.java | 26 ++ .../text/selection/CPPSelectionTestsIndexer.java | 34 ++ .../text/selection/CPPSelectionTestsNoIndexer.java | 29 ++ .../preferences/formatter/FormatterMessages.java | 8 + .../formatter/FormatterMessages.properties | 9 + .../preferences/formatter/WhiteSpaceOptions.java | 52 +++ 58 files changed, 3043 insertions(+), 163 deletions(-) create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCPPTypes.java create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCTypes.java create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/StructuredBindingTests.java create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingIndexTests.java create mode 100644 core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingTests.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTStructuredBindingDeclaration.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTStructuredBindingDeclaration.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPImplicitVariable.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPStructuredBindingComposite.java diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPAttributeTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPAttributeTests.java index 4ac1c542fc0..a579e8f1839 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPAttributeTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPAttributeTests.java @@ -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(); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCPPTypes.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCPPTypes.java new file mode 100644 index 00000000000..24f4fa96327 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCPPTypes.java @@ -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); + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCTypes.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCTypes.java new file mode 100644 index 00000000000..d1548dc2cd8 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommonCTypes.java @@ -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); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java index b7174a8adbb..5f17ae73f30 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java @@ -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; } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/SemanticTestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/SemanticTestBase.java index aff2182fed0..2c0eb1f6876 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/SemanticTestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/SemanticTestBase.java @@ -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); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaIndexTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaIndexTests.java index 25013a2e934..f2733100340 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaIndexTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaIndexTests.java @@ -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; /** diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaTests.java index dd16e67f70d..58c73597d6b 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/GenericLambdaTests.java @@ -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. diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/InitCaptureTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/InitCaptureTests.java index 2e38a3f274f..1dd1795e0b5 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/InitCaptureTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/InitCaptureTests.java @@ -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. diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionIndexTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionIndexTests.java index ebcbd0e93ff..0a152b27981 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionIndexTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionIndexTests.java @@ -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 { diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionTests.java index 6c86c338777..529e70904e4 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/ReturnTypeDeductionTests.java @@ -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 { diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/VariableTemplateTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/VariableTemplateTests.java index 599d01d92ad..df3a1b57109 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/VariableTemplateTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/VariableTemplateTests.java @@ -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; diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/AllConstexprEvalTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/AllConstexprEvalTests.java index 16deda4065f..79eb731d114 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/AllConstexprEvalTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/AllConstexprEvalTests.java @@ -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; } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/StructuredBindingTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/StructuredBindingTests.java new file mode 100644 index 00000000000..dd6b20c6cb9 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/StructuredBindingTests.java @@ -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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // template + // constexpr auto get(std::array const & values) { + // return values.elements[I]; + // } + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // static const size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static double value = static_cast(N); + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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 + // struct array { + // T elements[N]; + // template + // constexpr auto get() { + // return elements[I]; + // } + // }; + // size_t nonConstexprFunction() { + // return 3; + // } + // template + // struct tuple_size; + // template + // struct tuple_size> { + // static const size_t value = nonConstexprFunction(); + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + // } + // constexpr auto createValues() { + // std::array 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(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java index 3083093cf5d..1c473cfbfda 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java @@ -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(); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingIndexTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingIndexTests.java new file mode 100644 index 00000000000..ed3a76cc238 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingIndexTests.java @@ -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 + // struct array { + // T elements[N]; + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + //} + + //auto [f, s, t] = std::array{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 + // struct pair { + // T1 t1; + // T2 t2; + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = 2; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element_base { + // using type = T; + // }; + // template + // struct tuple_element<0, pair> : tuple_element_base { + // }; + // template + // struct tuple_element<1, pair> : tuple_element_base { + // }; + // template + // auto get(pair const & p) { + // if constexpr (I == 0) { + // return p.t1; + // } else { + // return p.t2; + // } + // } + //} + + //auto [a, b] = std::pair{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 + // struct array { + // T elements[N]; + // }; + // template + // struct tuple_size; + // template + // struct tuple_size> { + // constexpr static size_t value = N; + // }; + // template + // struct tuple_element; + // template + // struct tuple_element> { + // using type = T; + // }; + //} + //struct X { + // int first; + // int second; + //}; + + //int main() { + // auto arr = std::array{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_); + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingTests.java new file mode 100644 index 00000000000..5bbaebd81fd --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/StructuredBindingTests.java @@ -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); + 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 + // 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 + // auto get() { + // if constexpr (V == 0) { + // return first(); + // } else if (V == 1) { + // return second(); + // } + // static_assert(V < 2); + // } + //}; + // + //namespace std { + // template + // struct tuple_size; + // template <> + // struct tuple_size { + // constexpr static size_t value = 2; + // }; + // template + // 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 + //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 + // struct tuple_size; + // template <> + // struct tuple_size { + // constexpr static size_t value = 2; + // }; + // template + // 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()); + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexTests.java index 6543e396557..12a6ac0f346 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexTests.java @@ -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); diff --git a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterAttributeTestSource.awts b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterAttributeTestSource.awts index 64a636b5309..6b9412cb0de 100644 --- a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterAttributeTestSource.awts +++ b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterAttributeTestSource.awts @@ -376,4 +376,9 @@ int __declspec(selectany)* pi2 = 0; //!MS declspec attribute on simple declaration //%CPP GNU -__declspec(thread) int tls_i = 1; \ No newline at end of file +__declspec(thread) int tls_i = 1; + +//!Attributed structured binding declaration +//%CPP +int arr[]{1, 2, 3}; +[[nodiscard]] auto [a, b, c] = arr; diff --git a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterDeclarationTestSource.awts b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterDeclarationTestSource.awts index 0228c738221..e15e59ece6f 100644 --- a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterDeclarationTestSource.awts +++ b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterDeclarationTestSource.awts @@ -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{}; diff --git a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterStatementTestSource.awts b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterStatementTestSource.awts index 41cd52b2d28..64fe167a370 100644 --- a/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterStatementTestSource.awts +++ b/core/org.eclipse.cdt.core.tests/resources/rewrite/ASTWriterStatementTestSource.awts @@ -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){ + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index 36394ef428c..dc9f1a468d0 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -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" diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ISemanticProblem.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ISemanticProblem.java index 4e5f33bcb4c..3134e58fad9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ISemanticProblem.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ISemanticProblem.java @@ -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. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTStructuredBindingDeclaration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTStructuredBindingDeclaration.java new file mode 100644 index 00000000000..8eff34af474 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTStructuredBindingDeclaration.java @@ -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. + *

+ * Examples: + *

    + *
  • auto [x, y]{coordinate};
  • + *
  • auto & [x, y](coordinate);
  • + *
  • auto && [x, y] = createCoordinte();
  • + *
+ * + * @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 { + /** + * IDENTIFIER represents the relationship between an + * ICPPASTStructuredBindingDeclaration and its + * IASTNames. + */ + public static final ASTNodeProperty IDENTIFIER = new ASTNodeProperty( + "ICPPASTStructuredBindingDeclaration.IDENTIFIER - IASTName for ICPPASTStructuredBindingDeclaration"); //$NON-NLS-1$ + + /** + * INITIALIZER represents the relationship between an + * ICPPASTStructuredBindingDeclaration and its IASTInitializer. + */ + public static final ASTNodeProperty INITIALIZER = new ASTNodeProperty( + "ICPPASTStructuredBindingDeclaration.INITIALIZER - IASTInitializer for ICPPASTStructuredBindingDeclaration"); //$NON-NLS-1$ + + /** + * Returns the RefQualifier of the structured binding. For either lvalue or + * rvalue reference qualifiers. + *

+ * Examples: + *

    + *
  • For auto [x, y] = coordinate; it returns the empty Optional
  • + *
  • For auto & [x, y] = coordinate; it returns Optional.of(RefQualifier.LVALUE)
  • + *
  • For auto && [x, y] = createCoordinte(); it returns Optional.of(RefQualifier.RVALUE)
  • + *
+ * + * @return The returned RefQualifier of the C++ declaration or null 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. + *

+ * Example: For auto & [x, y] = coordinate; it returns the names x and y. + * + * @return All declared names of the structured binding asIASTName[] + * @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: + *

    + *
  • For auto [x, y]{coordinate}; it returns an ICPPASTInitializerList
  • + *
  • For auto & [x, y](coordinate); it returns an ICPPASTConstructorInitializer
  • + *
  • For auto && [x, y] = createCoordinte(); it returns an IASTEqualsInitializer
  • + *
+ * + * @return The IASTInitializer of this structured binding. It can be null if the C++ declaration is lacking an initializer. + * @see IASTInitializer + * @see ICPPASTInitializerList + * @see ICPPASTConstructorInitializer + * @see IASTEqualsInitializer + */ + @Nullable + IASTInitializer getInitializer(); +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPNodeFactory.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPNodeFactory.java index 995be0308b9..11af18db9fb 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPNodeFactory.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPNodeFactory.java @@ -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); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java index f5aaab64105..9698bfac8e5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java @@ -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 { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java index fab7f546028..cc215747ed2 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java @@ -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; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemType.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemType.java index e2a026f862a..24eeb562e32 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemType.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemType.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/VariableReadWriteFlags.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/VariableReadWriteFlags.java index d693071b73a..fb757d85f89 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/VariableReadWriteFlags.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/VariableReadWriteFlags.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTImplicitName.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTImplicitName.java index e8d638ce2b9..b15def18a0f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTImplicitName.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTImplicitName.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTSimpleDeclaration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTSimpleDeclaration.java index a7d76c5f1ff..5ab051c26a3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTSimpleDeclaration.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTSimpleDeclaration.java @@ -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 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 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTStructuredBindingDeclaration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTStructuredBindingDeclaration.java new file mode 100644 index 00000000000..41779d21ff6 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTStructuredBindingDeclaration.java @@ -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 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 }; + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPImplicitVariable.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPImplicitVariable.java new file mode 100644 index 00000000000..c4ec33b459a --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPImplicitVariable.java @@ -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]: + * auto [first, second] = decomposed; + * + * The initializerEvaluation 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(); + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNodeFactory.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNodeFactory.java index 176335f6c0b..b659a58cc17 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNodeFactory.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNodeFactory.java @@ -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); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPStructuredBindingComposite.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPStructuredBindingComposite.java new file mode 100644 index 00000000000..e9c1bbc85b1 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPStructuredBindingComposite.java @@ -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 ICPPEvaluation 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 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; + } + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPVariable.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPVariable.java index 9bce0ccd1fe..b77e1b87038 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPVariable.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPVariable.java @@ -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); } - return EvalFixed.INCOMPLETE; + + 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 null; } private static ICPPConstructor getImplicitlyCalledCtor(ICPPASTDeclarator declarator) { @@ -350,4 +378,5 @@ public class CPPVariable extends PlatformObject implements ICPPInternalDeclaredV public void allDeclarationsDefinitionsAdded() { fAllResolved = true; } + } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java index cf8815ba4fd..ebdf710df1d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java @@ -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 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 result = new ArrayList<>(); + result.add(identifier()); + while (LT(1) == IToken.tCOMMA) { + consume(); + result.add(identifier()); + } + return result.toArray(new IASTName[result.size()]); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/VariableHelpers.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/VariableHelpers.java index df7c37954fd..34adee0f83b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/VariableHelpers.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/VariableHelpers.java @@ -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); } } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java index 6728e38cb71..bcb87fc8d92 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java @@ -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 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 bindings) { + private static void findBindingsForQualifiedName(IScope scope, String qualifiedName, Collection 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); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVariableReadWriteFlags.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVariableReadWriteFlags.java index 6f8b4d518c0..e5ee18de9fa 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVariableReadWriteFlags.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVariableReadWriteFlags.java @@ -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 } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java index bff715b23a8..02d25f564e8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java @@ -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 IS_STATIC_VARIABLE = IVariable::isStatic; + private static final Predicate HAS_INTEGRAL_TYPE = variable -> BuiltinOperators + .isIntegral(SemanticUtil.getUltimateTypeUptoPointers(variable.getType())); + private static final Predicate HAS_CONST_TYPE = field -> ExpressionTypes.isConst(field.getType()); + private static final Predicate IS_NAMED_VALUE = binding -> binding.getName().equals(VALUE_STR); + + /** + * Required until Java 9 Optional.stream() + */ + private static final Stream toStream(Optional 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 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 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 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(); - } - } - 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 = getAutoInitClauseForRangeBasedFor((ICPPASTRangeBasedForStatement) parent); + if (autoInitClause == null) { + 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. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecIncomplete.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecIncomplete.java index 6ab1df368c3..6f307fd3e0e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecIncomplete.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecIncomplete.java @@ -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; } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExpressionTypes.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExpressionTypes.java index 2481dbbcb82..2f2f36c67b7 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExpressionTypes.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExpressionTypes.java @@ -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) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java index eaca924d507..f84d285bad6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/SemanticUtil.java @@ -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) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java index 9471f80791d..e99d4ecfd68 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java @@ -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(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java index 97d90062102..86eb87683a4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java @@ -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); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java index 5d9fa84b3a6..085d25f2129 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java @@ -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; + } + } + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java index 4752e5e0ab5..05b9367d2f2 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java @@ -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$ } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java index a4cfe659344..2557328d2e5 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterConstants.java @@ -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$ + /** + *
+	 * 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
+	 * 
+ * @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$ + /** + *
+	 * 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
+	 * 
+ * @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$ + /** + *
+	 * 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
+	 * 
+ * @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$ + /** + *
+	 * 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
+	 * 
+ * @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$ + /** + *
+	 * 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
+	 * 
+ * @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$ + /** + *
+	 * 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
+	 * 
+ * @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$ /** *
 	 * FORMATTER / Option to keep else statement on the same line
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
index 2db0bbd1290..dc6868739f5 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/DefaultCodeFormatterOptions.java
@@ -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;
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
index 40ecad720dd..af013b73cf7 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/CodeFormatterVisitor.java
@@ -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 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*/) {
diff --git a/core/org.eclipse.cdt.ui.tests/resources/ceditor/occurrences.cpp b/core/org.eclipse.cdt.ui.tests/resources/ceditor/occurrences.cpp
index c63250e4f5c..a0e7f2e4637 100644
--- a/core/org.eclipse.cdt.ui.tests/resources/ceditor/occurrences.cpp
+++ b/core/org.eclipse.cdt.ui.tests/resources/ceditor/occurrences.cpp
@@ -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;
 }
\ No newline at end of file
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
index 7fb8106301d..f7f52b193ad 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CodeFormatterTest.java
@@ -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();
+	}
 }
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/MarkOccurrenceTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/MarkOccurrenceTest.java
index aa4a5a9c594..a301f5ce5ea 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/MarkOccurrenceTest.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/MarkOccurrenceTest.java
@@ -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);
 
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsIndexer.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsIndexer.java
index fec4270ace7..a66e622f1de 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsIndexer.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsIndexer.java
@@ -1380,4 +1380,38 @@ public class CPPSelectionTestsIndexer extends BaseSelectionTestsIndexer {
 		assertInstance(target, IASTName.class);
 		assertEquals(IASTNameOwner.r_definition, ((IASTName) target).getRoleOfName(false));
 	}
+
+	//	template 
+	//	struct Node {
+	//		Node * next;
+	//		E value;
+	//	};
+	//	Node 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);
+	}
 }
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java
index 6e2d290a75a..6a96df10ae4 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/selection/CPPSelectionTestsNoIndexer.java
@@ -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));
+	}
 }
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
index f3f5ed02322..7deb8d1086b 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.java
@@ -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;
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
index 4bdc8611c37..6921dc869ed 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/FormatterMessages.properties
@@ -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
 
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/WhiteSpaceOptions.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/WhiteSpaceOptions.java
index 7336a30e56c..95e938b5ff6 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/WhiteSpaceOptions.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/formatter/WhiteSpaceOptions.java
@@ -236,6 +236,10 @@ public final class WhiteSpaceOptions {
 					"map 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 workingValues, final InnerNode parent) {
@@ -602,6 +611,9 @@ public final class WhiteSpaceOptions {
 	private void createBeforeClosingBracketTree(Map 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 workingValues, final InnerNode parent) {
@@ -616,6 +628,9 @@ public final class WhiteSpaceOptions {
 	private void createBeforeOpenBracketTree(Map 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 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 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 workingValues, final InnerNode parent) {
@@ -835,6 +857,9 @@ public final class WhiteSpaceOptions {
 	private void createAfterOpenBracketTree(Map 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 workingValues, final InnerNode parent) {
@@ -1268,6 +1293,33 @@ public final class WhiteSpaceOptions {
 		return root;
 	}
 
+	private void createStructuredBindingTree(Map 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 workingValues, InnerNode parent) {
 		final InnerNode root = new InnerNode(parent, workingValues,
 				FormatterMessages.WhiteSpaceTabPage_template_arguments);
-- 
cgit v1.2.3