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 5f17ae73f30..f1fe6b37975 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 @@ -20,6 +20,7 @@ import org.eclipse.cdt.core.parser.tests.ast2.cxx14.GenericLambdaTests; 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.DeductionGuideTests; 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; @@ -76,6 +77,7 @@ public class DOMParserTestSuite extends TestCase { suite.addTest(TemplateAutoTests.suite()); suite.addTestSuite(LambdaExpressionTests.class); suite.addTestSuite(StructuredBindingTests.class); + suite.addTestSuite(DeductionGuideTests.class); return suite; } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/DeductionGuideTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/DeductionGuideTests.java new file mode 100644 index 00000000000..02705d96e5b --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx17/DeductionGuideTests.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2018, Institute for Software and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Felix Morgner - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.parser.tests.ast2.cxx17; + +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasToString; +import static org.hamcrest.Matchers.is; + +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeductionGuide; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTestBase; +import org.eclipse.cdt.core.parser.util.ArrayUtil; +import org.hamcrest.MatcherAssert; + +public class DeductionGuideTests extends AST2CPPTestBase { + + private static ICPPASTDeductionGuide firstGuide(IASTDeclaration[] array) { + return (ICPPASTDeductionGuide) ArrayUtil.filter(array, d -> d instanceof ICPPASTDeductionGuide)[0]; + } + + // template struct U; + // U() -> U; + public void testDeductionGuideWithoutArguments() throws Exception { + IASTDeclaration[] declarations = parseAndCheckBindings().getDeclarations(); + + ICPPASTDeductionGuide guide = firstGuide(declarations); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(0))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // template struct U; + // U(int, float) -> U; + public void testDeductionGuideWithArguments() throws Exception { + IASTDeclaration[] declarations = parseAndCheckBindings().getDeclarations(); + + ICPPASTDeductionGuide guide = firstGuide(declarations); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(2))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // template struct U; + // template + // U(T) -> U; + public void testDeductionGuideTemplate() throws Exception { + IASTDeclaration[] declarations = parseAndCheckBindings().getDeclarations(); + ICPPASTTemplateDeclaration template = (ICPPASTTemplateDeclaration) declarations[1]; + ICPPASTDeductionGuide guide = (ICPPASTDeductionGuide) template.getDeclaration(); + + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(1))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // template struct U; + // explicit U() -> U; + public void testExplicitDeductionGuide() throws Exception { + IASTDeclaration[] declarations = parseAndCheckBindings().getDeclarations(); + + ICPPASTDeductionGuide guide = firstGuide(declarations); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(0))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(true))); + } + + // template struct U; + // template + // explicit U(T) -> U; + public void testExplicitDeductionGuideTemplate() throws Exception { + IASTDeclaration[] declarations = parseAndCheckBindings().getDeclarations(); + ICPPASTTemplateDeclaration template = (ICPPASTTemplateDeclaration) declarations[1]; + ICPPASTDeductionGuide guide = (ICPPASTDeductionGuide) template.getDeclaration(); + + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(1))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(true))); + } + + // struct S { + // template struct U; + // U() -> U; + // }; + public void testDeductionGuideWithoutArgumentsForNestedClassType() throws Exception { + IASTTranslationUnit tu = parseAndCheckBindings(); + IASTSimpleDeclaration declaration = (IASTSimpleDeclaration) tu.getDeclarations()[0]; + ICPPASTCompositeTypeSpecifier struct = (ICPPASTCompositeTypeSpecifier) declaration.getDeclSpecifier(); + IASTDeclaration[] members = struct.getMembers(); + + ICPPASTDeductionGuide guide = firstGuide(members); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(0))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // struct S { + // template struct U; + // U(char, bool) -> U; + // }; + public void testDeductionGuideWithArgumentsForNestedClassType() throws Exception { + IASTTranslationUnit tu = parseAndCheckBindings(); + IASTSimpleDeclaration declaration = (IASTSimpleDeclaration) tu.getDeclarations()[0]; + ICPPASTCompositeTypeSpecifier struct = (ICPPASTCompositeTypeSpecifier) declaration.getDeclSpecifier(); + IASTDeclaration[] members = struct.getMembers(); + + ICPPASTDeductionGuide guide = firstGuide(members); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(2))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // struct S { + // template struct U; + // template + // U(T) -> U; + // }; + public void testDeductionGuideTemplateWithArgumentsForNestedClassType() throws Exception { + IASTTranslationUnit tu = parseAndCheckBindings(); + IASTSimpleDeclaration declaration = (IASTSimpleDeclaration) tu.getDeclarations()[0]; + ICPPASTCompositeTypeSpecifier struct = (ICPPASTCompositeTypeSpecifier) declaration.getDeclSpecifier(); + IASTDeclaration[] members = struct.getMembers(); + ICPPASTTemplateDeclaration template = (ICPPASTTemplateDeclaration) members[1]; + + ICPPASTDeductionGuide guide = (ICPPASTDeductionGuide) template.getDeclaration(); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(1))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(false))); + } + + // struct S { + // template struct U; + // explicit U() -> U; + // }; + public void testExplicitDeductionGuideForNestedClassType() throws Exception { + IASTTranslationUnit tu = parseAndCheckBindings(); + IASTSimpleDeclaration declaration = (IASTSimpleDeclaration) tu.getDeclarations()[0]; + ICPPASTCompositeTypeSpecifier struct = (ICPPASTCompositeTypeSpecifier) declaration.getDeclSpecifier(); + IASTDeclaration[] members = struct.getMembers(); + + ICPPASTDeductionGuide guide = firstGuide(members); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(0))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(true))); + } + + // struct S { + // template struct U; + // template + // explicit U(T) -> U; + // }; + public void testExplicitDeductionGuideTemplateForNestedClassType() throws Exception { + IASTTranslationUnit tu = parseAndCheckBindings(); + IASTSimpleDeclaration declaration = (IASTSimpleDeclaration) tu.getDeclarations()[0]; + ICPPASTCompositeTypeSpecifier struct = (ICPPASTCompositeTypeSpecifier) declaration.getDeclSpecifier(); + IASTDeclaration[] members = struct.getMembers(); + ICPPASTTemplateDeclaration template = (ICPPASTTemplateDeclaration) members[1]; + + ICPPASTDeductionGuide guide = (ICPPASTDeductionGuide) template.getDeclaration(); + MatcherAssert.assertThat(guide.getParameters(), is(arrayWithSize(1))); + MatcherAssert.assertThat(guide.getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.getSimpleTemplateId().getTemplateName(), hasToString("U")); + MatcherAssert.assertThat(guide.isExplicit(), is(equalTo(true))); + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelBuilder2.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelBuilder2.java index 63c983b1b11..4364123abb1 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelBuilder2.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelBuilder2.java @@ -55,6 +55,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeductionGuide; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; @@ -352,6 +353,8 @@ public class CModelBuilder2 implements IContributedModelBuilder { // TODO [cmodel] problem declaration? } else if (declaration instanceof ICPPASTStaticAssertDeclaration) { // ignore + } else if (declaration instanceof ICPPASTDeductionGuide) { + // TODO [cmodel] deduction guide? } else { assert false : "TODO: " + declaration.getClass().getName(); //$NON-NLS-1$ } @@ -416,6 +419,8 @@ public class CModelBuilder2 implements IContributedModelBuilder { createTemplateDeclaration(parent, (ICPPASTTemplateDeclaration) declaration); } else if (declaration instanceof IASTProblemDeclaration) { // ignore problem declarations (or create special elements for debugging?) + } else if (declaration instanceof ICPPASTDeductionGuide) { + // TODO [cmodel] deduction guide? } else { assert false : "TODO: " + declaration.getClass().getName(); //$NON-NLS-1$ } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTDeductionGuide.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTDeductionGuide.java new file mode 100644 index 00000000000..0d455df90c5 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTDeductionGuide.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2018, Institute for Software 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 + * + * Contributors: + * Felix Morgner - 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.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTName; + +/** + * A C++ class-template argument deduction guide + *

+ * e.g: + * + *

+ * SomeTemplateName(int) -> SomeTemplateName<float>;
+ *
+ * template<typename I>
+ * SomeTemplateName(I, I) -> SomeTemplateName<typename std::iterator_traits<I>::value_type>;
+ * 
+ *

+ * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @since 6.11 + */ +public interface ICPPASTDeductionGuide extends IASTDeclaration, ICPPASTParameterListOwner { + + /** + * TEMPLATE_NAME represents the relationship between an + * ICPPASTDeductionGuide and it's nested + * IASTName. + */ + static final ASTNodeProperty TEMPLATE_NAME = new ASTNodeProperty( + "ICPPASTDeductionGuide.TEMPLATE_NAME - IASTName for ICPPASTDeductionGuide"); //$NON-NLS-1$ + + /** + * TEMPLATE_ID represents the relationship between an + * ICPPASTDeductionGuide and it's nested + * ICPPASTTemplateId. + */ + static final ASTNodeProperty TEMPLATE_ID = new ASTNodeProperty( + "ICPPASTDeductionGuide.TEMPLATE_ID - ICPPASTTemplateId for ICPPASTDeductionGuide"); //$NON-NLS-1$ + + /** + * PARAMETER represents the relationship between an + * ICPPASTDeductionGuide and it's nested + * IASTParameterDeclaration. + */ + public final static ASTNodeProperty PARAMETER = new ASTNodeProperty( + "ICPPASTDeductionGuide.PARAMETER - IASTParameterDeclaration for ICPPASTDeductionGuide"); //$NON-NLS-1$ + + /** + * Check if the deduction guide was declared as 'explicit'. + */ + boolean isExplicit(); + + /** + * Set whether or not the deduction guide is marked explicit + */ + void setExplicit(boolean isExplict); + + /** + * Get the name of the template type the deduction guide refers to + */ + IASTName getTemplateName(); + + /** + * Set the name of the template type the deduction guide refers to + */ + void setTemplateName(IASTName name); + + /** + * Get the template id of the deduction guide + */ + ICPPASTTemplateId getSimpleTemplateId(); + + /** + * Set the template id of the deduction guide + */ + void setSimpleTemplateId(ICPPASTTemplateId id); + + @Override + public ICPPASTDeductionGuide copy(); + + @Override + public ICPPASTDeductionGuide copy(CopyStyle style); + +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTFunctionDeclarator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTFunctionDeclarator.java index e2be17585fd..3a6d6a297a8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTFunctionDeclarator.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTFunctionDeclarator.java @@ -29,7 +29,8 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; * @noimplement This interface is not intended to be implemented by clients. * @noextend This interface is not intended to be extended by clients. */ -public interface ICPPASTFunctionDeclarator extends IASTStandardFunctionDeclarator, ICPPASTDeclarator { +public interface ICPPASTFunctionDeclarator + extends IASTStandardFunctionDeclarator, ICPPASTDeclarator, ICPPASTParameterListOwner { /** * @since 5.9 */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTParameterListOwner.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTParameterListOwner.java new file mode 100644 index 00000000000..2fb01c1f04f --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTParameterListOwner.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2018, Institute for Software and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Felix Morgner - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.dom.ast.cpp; + +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; + +/** + * Internal interface to describe the ability of having a parameter list + * @since 6.11 + */ +public interface ICPPASTParameterListOwner { + + /** + * Add a parameter to the parameter list of the parameter list owner. + */ + public void addParameterDeclaration(IASTParameterDeclaration parameter); + + /** + * Gets the parameter declarations for the parameter list owner + */ + public IASTParameterDeclaration[] getParameters(); + + /** + * Set whether or not the parameter list owner takes a variable number of + * arguments. + */ + public void setVarArgs(boolean value); + + /** + * Check if the parameter list owner takes a variable number of arguments. + */ + public boolean takesVarArgs(); +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/DeclarationOptions.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/DeclarationOptions.java index 1c8285c104b..50376275f05 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/DeclarationOptions.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/DeclarationOptions.java @@ -34,14 +34,15 @@ public class DeclarationOptions { final public static int SINGLE_DTOR = 0x4000; final public static int ALLOW_FUNCTION_DEFINITION = 0x8000; final public static int NO_COMPOSITE_SPECIFIER = 0x10000; + final public static int ALLOW_DEDUCTION_GUIDE = 0x20000; public static final DeclarationOptions GLOBAL = new DeclarationOptions( - ALLOW_EMPTY_SPECIFIER | ALLOW_OPAQUE_ENUM | ALLOW_FUNCTION_DEFINITION), + ALLOW_EMPTY_SPECIFIER | ALLOW_OPAQUE_ENUM | ALLOW_FUNCTION_DEFINITION | ALLOW_DEDUCTION_GUIDE), FUNCTION_STYLE_ASM = new DeclarationOptions( ALLOW_EMPTY_SPECIFIER | NO_INITIALIZER | ALLOW_ABSTRACT | ALLOW_FUNCTION_DEFINITION), C_MEMBER = new DeclarationOptions(ALLOW_BITFIELD | ALLOW_ABSTRACT), CPP_MEMBER = new DeclarationOptions(ALLOW_EMPTY_SPECIFIER | ALLOW_BITFIELD | ALLOW_OPAQUE_ENUM - | NO_CTOR_STYLE_INITIALIZER | ALLOW_FUNCTION_DEFINITION), + | NO_CTOR_STYLE_INITIALIZER | ALLOW_FUNCTION_DEFINITION | ALLOW_DEDUCTION_GUIDE), LOCAL = new DeclarationOptions(ALLOW_OPAQUE_ENUM), PARAMETER = new DeclarationOptions(ALLOW_ABSTRACT | ALLOW_PARAMETER_PACKS | REQUIRE_SIMPLE_NAME | NO_BRACED_INITIALIZER | NO_CTOR_STYLE_INITIALIZER), diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java index 29590cb6bcc..5fc12dbdfd0 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguousTemplateArgument.java @@ -47,6 +47,7 @@ public class CPPASTAmbiguousTemplateArgument extends ASTAmbiguousNode implements fNodes = new ArrayList<>(2); for (IASTNode node : nodes) { if (node instanceof IASTTypeId || node instanceof IASTExpression) { + node.setParent(this); fNodes.add(node); } else { Assert.isLegal(false, node == null ? "null" : node.getClass().getName()); //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTDeductionGuide.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTDeductionGuide.java new file mode 100644 index 00000000000..edcfcddb3f4 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTDeductionGuide.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2018, Institute for Software and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Felix Morgner - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.core.dom.parser.cpp; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeductionGuide; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; +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.IASTAmbiguityParent; + +public class CPPASTDeductionGuide extends ASTNode implements ICPPASTDeductionGuide, IASTAmbiguityParent { + + private IASTParameterDeclaration[] parameters; + private IASTName templateName; + private ICPPASTTemplateId templateId; + private boolean takesVarArgs; + private boolean isExplicit; + + @Override + public boolean accept(ASTVisitor visitor) { + if (visitor.shouldVisitDeclarations) { + switch (visitor.visit(this)) { + case ASTVisitor.PROCESS_ABORT: + return false; + case ASTVisitor.PROCESS_SKIP: + return true; + default: + break; + } + } + + if (!templateName.accept(visitor)) { + return false; + } + + if (parameters != null) { + for (IASTParameterDeclaration parameter : parameters) { + if (parameter != null && !parameter.accept(visitor)) { + return false; + } + } + } + + if (!templateId.accept(visitor)) { + return false; + } + + if (visitor.shouldVisitDeclarations) { + switch (visitor.leave(this)) { + case ASTVisitor.PROCESS_ABORT: + return false; + case ASTVisitor.PROCESS_SKIP: + return true; + default: + break; + } + } + return true; + } + + @Override + public void addParameterDeclaration(IASTParameterDeclaration parameter) { + assertNotFrozen(); + assert (parameter != null); + parameter.setParent(this); + parameter.setPropertyInParent(PARAMETER); + parameters = ArrayUtil.append(IASTParameterDeclaration.class, parameters, parameter); + } + + @Override + public IASTParameterDeclaration[] getParameters() { + if (parameters == null) { + return ICPPASTParameterDeclaration.EMPTY_CPPPARAMETERDECLARATION_ARRAY; + } + return ArrayUtil.trim(parameters); + } + + @Override + public void setVarArgs(boolean value) { + assertNotFrozen(); + takesVarArgs = value; + } + + @Override + public boolean takesVarArgs() { + return takesVarArgs; + } + + @Override + public boolean isExplicit() { + return isExplicit; + } + + @Override + public void setExplicit(boolean value) { + assertNotFrozen(); + isExplicit = value; + } + + @Override + public IASTName getTemplateName() { + return templateName; + } + + @Override + public void setTemplateName(IASTName name) { + assertNotFrozen(); + assert (name != null); + name.setParent(this); + name.setPropertyInParent(TEMPLATE_NAME); + templateName = name; + } + + @Override + public ICPPASTTemplateId getSimpleTemplateId() { + return templateId; + } + + @Override + public void setSimpleTemplateId(ICPPASTTemplateId id) { + assertNotFrozen(); + assert (id != null); + id.setParent(this); + id.setPropertyInParent(TEMPLATE_ID); + templateId = id; + } + + @Override + public ICPPASTDeductionGuide copy() { + return copy(CopyStyle.withoutLocations); + } + + @Override + public ICPPASTDeductionGuide copy(CopyStyle style) { + CPPASTDeductionGuide copy = new CPPASTDeductionGuide(); + copy.isExplicit = isExplicit; + copy.takesVarArgs = takesVarArgs; + copy.setTemplateName(templateName.copy(style)); + copy.setSimpleTemplateId(templateId.copy(style)); + if (parameters != null) { + for (IASTParameterDeclaration parameter : parameters) { + if (parameter != null) { + copy.addParameterDeclaration(parameter.copy(style)); + } + } + } + return super.copy(copy, style); + } + + @Override + public void replace(IASTNode child, IASTNode other) { + int indexOfChild = ArrayUtil.indexOfEqual(parameters, child); + if (indexOfChild > -1) { + other.setParent(this); + other.setPropertyInParent(PARAMETER); + child.setParent(null); + parameters[indexOfChild] = (IASTParameterDeclaration) other; + } + } +} 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 58686ecc0b4..6e04ad4ac2f 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 @@ -127,6 +127,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTOperatorName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPackExpandable; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterListOwner; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator; @@ -3055,6 +3056,35 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { throwBacktrack(LA(1)); } + if (dtor instanceof ICPPASTFunctionDeclarator + && ((ICPPASTFunctionDeclarator) dtor).getTrailingReturnType() != null) { + if (declSpec instanceof IASTSimpleDeclSpecifier + && ((IASTSimpleDeclSpecifier) declSpec).getType() == IASTSimpleDeclSpecifier.t_unspecified) { + // we encountered something that looks like a ctor with trailing return type + CPPASTDeductionGuide guide = new CPPASTDeductionGuide(); + guide.setExplicit(((ICPPASTDeclSpecifier) declSpec).isExplicit()); + guide.setTemplateName(dtor.getName()); + ICPPASTParameterDeclaration[] params = ((ICPPASTFunctionDeclarator) dtor).getParameters(); + for (ICPPASTParameterDeclaration p : params) + guide.addParameterDeclaration(p); + guide.setVarArgs(((ICPPASTFunctionDeclarator) dtor).takesVarArgs()); + IASTTypeId nameSpecifier = ((ICPPASTFunctionDeclarator) dtor).getTrailingReturnType(); + IASTDeclSpecifier guideDeclSpec = nameSpecifier.getDeclSpecifier(); + if (guideDeclSpec instanceof ICPPASTNamedTypeSpecifier) { + IASTName n = ((ICPPASTNamedTypeSpecifier) guideDeclSpec).getName(); + if (n instanceof ICPPASTTemplateId) { + guide.setSimpleTemplateId((ICPPASTTemplateId) n); + } else { + throwBacktrack(dtor); + } + } else { + throwBacktrack(dtor); + } + setRange(guide, firstOffset, endOffset); + return guide; + } + } + // no function body final boolean isAmbiguous = altDeclSpec != null && altDtor != null && declarators.length == 1; @@ -3983,6 +4013,20 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { } } + //Check if it's a possible C++17 deduction guide + if (dtor instanceof ICPPASTFunctionDeclarator) { + ICPPASTFunctionDeclarator possibleGuide = (ICPPASTFunctionDeclarator) dtor; + IASTTypeId returnType = possibleGuide.getTrailingReturnType(); + if (returnType != null) { + IASTDeclSpecifier declSpec = returnType.getDeclSpecifier(); + if (declSpec instanceof ICPPASTNamedTypeSpecifier) { + IASTName n = ((ICPPASTNamedTypeSpecifier) declSpec).getName(); + if (n instanceof ICPPASTTemplateId) + return; + } + } + } + ASTNode node = (ASTNode) dtor; throwBacktrack(node.getOffset(), node.getLength()); } @@ -4755,44 +4799,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { int endOffset = last.getEndOffset(); final ICPPASTFunctionDeclarator fc = getNodeFactory().newFunctionDeclarator(null); - ICPPASTParameterDeclaration pd = null; - paramLoop: while (true) { - switch (LT(1)) { - case IToken.tRPAREN: - case IToken.tEOC: - endOffset = consume().getEndOffset(); - break paramLoop; - case IToken.tELLIPSIS: - consume(); - endOffset = consume(IToken.tRPAREN).getEndOffset(); - fc.setVarArgs(true); - break paramLoop; - case IToken.tCOMMA: - if (pd == null) - throwBacktrack(LA(1)); - endOffset = consume().getEndOffset(); - pd = null; - break; - default: - if (pd != null) - throwBacktrack(startOffset, endOffset - startOffset); - - pd = parameterDeclaration(); - fc.addParameterDeclaration(pd); - endOffset = calculateEndOffset(pd); - break; - } - } - // Handle ambiguity between parameter pack and varargs. - if (pd != null) { - ICPPASTDeclarator dtor = pd.getDeclarator(); - if (dtor != null && !(dtor instanceof IASTAmbiguousDeclarator)) { - if (dtor.declaresParameterPack() && dtor.getNestedDeclarator() == null && dtor.getInitializer() == null - && dtor.getName().getSimpleID().length == 0) { - ((IASTAmbiguityParent) fc).replace(pd, new CPPASTAmbiguousParameterDeclaration(pd)); - } - } - } + endOffset = parameterList(startOffset, endOffset, fc); // Consume any number of __attribute__ tokens after the parameters List attributes = __attribute_decl_seq(supportAttributeSpecifiers, false); @@ -4897,6 +4904,58 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return setRange(fc, startOffset, endOffset); } + /** + * Parse a paramter list + * @param start Start offset + * @param end End offset + * @param owner The list owner + * @return The end offset after parsing + * @throws EndOfFileException + * @throws BacktrackException + */ + private int parameterList(int start, int end, ICPPASTParameterListOwner owner) + throws EndOfFileException, BacktrackException { + ICPPASTParameterDeclaration pd = null; + paramLoop: while (true) { + switch (LT(1)) { + case IToken.tRPAREN: + case IToken.tEOC: + end = consume().getEndOffset(); + break paramLoop; + case IToken.tELLIPSIS: + consume(); + end = consume(IToken.tRPAREN).getEndOffset(); + owner.setVarArgs(true); + break paramLoop; + case IToken.tCOMMA: + if (pd == null) + throwBacktrack(LA(1)); + end = consume().getEndOffset(); + pd = null; + break; + default: + if (pd != null) + throwBacktrack(start, end - start); + + pd = parameterDeclaration(); + owner.addParameterDeclaration(pd); + end = calculateEndOffset(pd); + break; + } + } + // Handle ambiguity between parameter pack and varargs. + if (pd != null) { + ICPPASTDeclarator dtor = pd.getDeclarator(); + if (dtor != null && !(dtor instanceof IASTAmbiguousDeclarator)) { + if (dtor.declaresParameterPack() && dtor.getNestedDeclarator() == null && dtor.getInitializer() == null + && dtor.getName().getSimpleID().length == 0) { + ((IASTAmbiguityParent) owner).replace(pd, new CPPASTAmbiguousParameterDeclaration(pd)); + } + } + } + return end; + } + /** * Parse an array declarator starting at the square bracket. */ 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 11cc4769f92..813e5a568c6 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 @@ -117,6 +117,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeductionGuide; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation; @@ -859,6 +860,9 @@ public class CPPVisitor extends ASTQueries { return null; } else if (parent instanceof ICPPASTTemplateDeclaration) { return CPPTemplates.createBinding(param); + } else if (parent instanceof ICPPASTDeductionGuide) { + ICPPASTDeductionGuide guide = (ICPPASTDeductionGuide) parent; + return new CPPParameter(name, findParameterIndex(param, guide.getParameters())); } return new ProblemBinding(name, IProblemBinding.SEMANTIC_INVALID_TYPE); } 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 0c2e2d6e25e..274995ba64c 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 @@ -1622,6 +1622,32 @@ public class DefaultCodeFormatterConstants { */ public static final String FORMATTER_INSERT_SPACE_BEFORE_LAMBDA_RETURN = CCorePlugin.PLUGIN_ID + ".formatter.insert_space_before_lambda_return"; //$NON-NLS-1$ + /** + *
+	 * FORMATTER / Option to insert a space after deduction guide arrow
+	 *     - option id:         "org.eclipse.cdt.core.formatter.insert_space_after_deduction_guide_arrow"
+	 *     - possible values:   { INSERT, DO_NOT_INSERT }
+	 *     - default:           INSERT
+	 * 
+ * @see CCorePlugin#INSERT + * @see CCorePlugin#DO_NOT_INSERT + * @since 6.11 + */ + public static final String FORMATTER_INSERT_SPACE_AFTER_DEDUCTION_GUIDE_ARROW = CCorePlugin.PLUGIN_ID + + ".formatter.insert_space_after_deduction_guide_arrow"; //$NON-NLS-1$ + /** + *
+	 * FORMATTER / Option to insert a space before deduction guide arrow
+	 *     - option id:         "org.eclipse.cdt.core.formatter.insert_space_before_deduction_guide_arrow"
+	 *     - possible values:   { INSERT, DO_NOT_INSERT }
+	 *     - default:           INSERT
+	 * 
+ * @see CCorePlugin#INSERT + * @see CCorePlugin#DO_NOT_INSERT + * @since 6.11 + */ + public static final String FORMATTER_INSERT_SPACE_BEFORE_DEDUCTION_GUIDE_ARROW = CCorePlugin.PLUGIN_ID + + ".formatter.insert_space_before_deduction_guide_arrow"; //$NON-NLS-1$ /** *
 	 * FORMATTER / Option to insert a space before an assignment operator
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 dc6868739f5..6d8782c285c 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
@@ -224,6 +224,14 @@ public class DefaultCodeFormatterOptions {
 	 * @since 6.8
 	 */
 	public boolean insert_space_before_lambda_return;
+	/**
+	 * @since 6.11
+	 */
+	public boolean insert_space_after_deduction_guide_arrow;
+	/**
+	 * @since 6.11
+	 */
+	public boolean insert_space_before_deduction_guide_arrow;
 	public boolean insert_space_before_assignment_operator;
 	public boolean insert_space_before_binary_operator;
 	public boolean insert_space_before_closing_angle_bracket_in_template_arguments;
@@ -630,6 +638,10 @@ public class DefaultCodeFormatterOptions {
 				this.insert_space_after_lambda_return ? CCorePlugin.INSERT : CCorePlugin.DO_NOT_INSERT);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_LAMBDA_RETURN,
 				this.insert_space_before_lambda_return ? CCorePlugin.INSERT : CCorePlugin.DO_NOT_INSERT);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_DEDUCTION_GUIDE_ARROW,
+				this.insert_space_after_deduction_guide_arrow ? CCorePlugin.INSERT : CCorePlugin.DO_NOT_INSERT);
+		options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_DEDUCTION_GUIDE_ARROW,
+				this.insert_space_before_deduction_guide_arrow ? CCorePlugin.INSERT : CCorePlugin.DO_NOT_INSERT);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR,
 				this.insert_space_before_assignment_operator ? CCorePlugin.INSERT : CCorePlugin.DO_NOT_INSERT);
 		options.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_BINARY_OPERATOR,
@@ -1756,6 +1768,18 @@ public class DefaultCodeFormatterOptions {
 		if (insertSpaceBeforeLambdaReturnOption != null) {
 			this.insert_space_before_lambda_return = CCorePlugin.INSERT.equals(insertSpaceBeforeLambdaReturnOption);
 		}
+		final Object insertSpaceAfterDeductionGuideOption = settings
+				.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_DEDUCTION_GUIDE_ARROW);
+		if (insertSpaceAfterDeductionGuideOption != null) {
+			this.insert_space_after_deduction_guide_arrow = CCorePlugin.INSERT
+					.equals(insertSpaceAfterDeductionGuideOption);
+		}
+		final Object insertSpaceBeforeDeductionGuideOption = settings
+				.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_DEDUCTION_GUIDE_ARROW);
+		if (insertSpaceBeforeDeductionGuideOption != null) {
+			this.insert_space_before_deduction_guide_arrow = CCorePlugin.INSERT
+					.equals(insertSpaceBeforeDeductionGuideOption);
+		}
 		final Object insertSpaceBeforeAssignmentOperatorOption = settings
 				.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR);
 		if (insertSpaceBeforeAssignmentOperatorOption != null) {
@@ -2438,6 +2462,8 @@ public class DefaultCodeFormatterOptions {
 		this.insert_space_after_unary_operator = false;
 		this.insert_space_after_lambda_return = true;
 		this.insert_space_before_lambda_return = true;
+		this.insert_space_after_deduction_guide_arrow = true;
+		this.insert_space_before_deduction_guide_arrow = true;
 		this.insert_space_before_assignment_operator = true;
 		this.insert_space_before_binary_operator = true;
 		this.insert_space_before_closing_angle_bracket_in_template_arguments = 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 2e3ce60c7a8..70fbedd4890 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
@@ -122,6 +122,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDecltypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeductionGuide;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDesignatedInitializer;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDesignator;
@@ -724,6 +725,8 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 				return visit((IASTFunctionDefinition) node);
 			} else if (node instanceof IASTSimpleDeclaration) {
 				return visit((IASTSimpleDeclaration) node);
+			} else if (node instanceof ICPPASTDeductionGuide) {
+				return visit((ICPPASTDeductionGuide) node);
 			} else if (node instanceof IASTASMDeclaration) {
 				return visit((IASTASMDeclaration) node);
 			} else if (node instanceof ICPPASTVisibilityLabel) {
@@ -1987,6 +1990,26 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
 		return PROCESS_SKIP;
 	}
 
+	private int visit(ICPPASTDeductionGuide node) {
+		node.getTemplateName().accept(this);
+		final List parameters = Arrays.asList(node.getParameters());
+		Runnable tailFormatter = new TrailingSemicolonFormatter(node);
+		final ListOptions options = new ListOptions(preferences.alignment_for_declarator_list);
+		options.fSpaceAfterSeparator = preferences.insert_space_after_comma_in_declarator_list;
+		options.fSpaceBeforeSeparator = preferences.insert_space_before_comma_in_declarator_list;
+		formatList(parameters, options, true, false, null);
+
+		if (peekNextToken() == Token.tARROW) {
+			scribe.printNextToken(Token.tARROW, preferences.insert_space_before_deduction_guide_arrow);
+			if (preferences.insert_space_after_deduction_guide_arrow) {
+				scribe.space();
+			}
+		}
+		node.getSimpleTemplateId().accept(this);
+		tailFormatter.run();
+		return PROCESS_SKIP;
+	}
+
 	private int visit(IASTSimpleDeclaration node) {
 		if (node instanceof ICPPASTStructuredBindingDeclaration) {
 			return visit((ICPPASTStructuredBindingDeclaration) node);
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 88a6229a737..74997c84ac7 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
@@ -4734,4 +4734,41 @@ public class CodeFormatterTest extends BaseUITestCase {
 	public void testFormatterOnOff_Bug559669() throws Exception {
 		assertFormatterResult();
 	}
+
+	//template
+	//struct U;
+	//U()->U;
+
+	//template
+	//struct U;
+	//U() -> U;
+	public void testDeductionGuide() throws Exception {
+		assertFormatterResult();
+	}
+
+	//template
+	//struct U;
+	//template
+	//U(C)->U;
+
+	//template
+	//struct U;
+	//template
+	//U(C) -> U;
+	public void testDeductionGuideTemplate() throws Exception {
+		assertFormatterResult();
+	}
+
+	//template
+	//struct U;
+	//template
+	//explicit U(C)->U;
+
+	//template
+	//struct U;
+	//template
+	//explicit U(C) -> U;
+	public void testDeductionGuideExplicit() throws Exception {
+		assertFormatterResult();
+	}
 }
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 7deb8d1086b..82737ec5982 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
@@ -212,6 +212,10 @@ final class FormatterMessages extends NLS {
 	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 WhiteSpaceTabPage_deduction_guide_declarations;
+	public static String WhiteSpaceTabPage_deduction_guide_before_arrow;
+	public static String WhiteSpaceTabPage_deduction_guide_after_arrow;
+	public static String WhiteSpaceOptions_deduction_guide_arrow_operator;
 	//	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 6921dc869ed..94d45b8107f 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
@@ -34,6 +34,10 @@ WhiteSpaceTabPage_lambda_expressions=Lambda expressions
 WhiteSpaceTabPage_lambda_before_return=before return arrow
 WhiteSpaceTabPage_lambda_after_return=after return arrow
 
+WhiteSpaceTabPage_deduction_guide_declarations=Deduction guide
+WhiteSpaceTabPage_deduction_guide_before_arrow=before arrow
+WhiteSpaceTabPage_deduction_guide_after_arrow=after arrow
+
 WhiteSpaceTabPage_classes=Types
 WhiteSpaceTabPage_classes_before_opening_brace_of_a_class=before opening brace of a class
 WhiteSpaceTabPage_classes_before_colon_of_base_clause=before colon of base clause
@@ -146,6 +150,7 @@ WhiteSpaceOptions_unary_operator=Unary operator
 WhiteSpaceOptions_prefix_operator=Prefix operator
 WhiteSpaceOptions_postfix_operator=Postfix operator
 WhiteSpaceOptions_lambda_arrow_operator=Lambda arrow operator
+WhiteSpaceOptions_deduction_guide_arrow_operator=Deduction guide arrow operator
 
 WhiteSpaceOptions_pointer=Pointer
 WhiteSpaceOptions_before_pointer=Before pointer
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 95e938b5ff6..78116c21743 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
@@ -183,6 +183,9 @@ public final class WhiteSpaceOptions {
 	private final PreviewSnippet LAMBDA_PREVIEW = new PreviewSnippet(CodeFormatter.K_CLASS_BODY_DECLARATIONS,
 			"void foo() { auto f = []()->int{return 0;};}"); //$NON-NLS-1$
 
+	private final PreviewSnippet DEDUCTION_GUIDE_PREVIEW = new PreviewSnippet(CodeFormatter.K_CLASS_BODY_DECLARATIONS,
+			"template struct U;\ntemplate\nU(C) -> U;"); //$NON-NLS-1$
+
 	private final PreviewSnippet INITIALIZER_LIST_PREVIEW = new PreviewSnippet(CodeFormatter.K_STATEMENTS,
 			"int array[]= {1, 2, 3};"); //$NON-NLS-1$
 
@@ -478,6 +481,7 @@ public final class WhiteSpaceOptions {
 		createLinkageTree(workingValues, declarations);
 		//        createConstructorTree(workingValues, declarations);
 		createLambdaDeclTree(workingValues, declarations);
+		createDeductionDeclTree(workingValues, declarations);
 		createMethodDeclTree(workingValues, declarations);
 		createExceptionSpecificationTree(workingValues, declarations);
 		createLabelTree(workingValues, declarations);
@@ -606,6 +610,9 @@ public final class WhiteSpaceOptions {
 				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_POSTFIX_OPERATOR, OPERATOR_PREVIEW);
 		createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_lambda_arrow_operator,
 				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_LAMBDA_RETURN, LAMBDA_PREVIEW);
+		createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_deduction_guide_arrow_operator,
+				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_DEDUCTION_GUIDE_ARROW,
+				DEDUCTION_GUIDE_PREVIEW);
 	}
 
 	private void createBeforeClosingBracketTree(Map workingValues, final InnerNode parent) {
@@ -852,6 +859,9 @@ public final class WhiteSpaceOptions {
 				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_POSTFIX_OPERATOR, OPERATOR_PREVIEW);
 		createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_lambda_arrow_operator,
 				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_LAMBDA_RETURN, LAMBDA_PREVIEW);
+		createOption(parent, workingValues, FormatterMessages.WhiteSpaceOptions_deduction_guide_arrow_operator,
+				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_DEDUCTION_GUIDE_ARROW,
+				DEDUCTION_GUIDE_PREVIEW);
 	}
 
 	private void createAfterOpenBracketTree(Map workingValues, final InnerNode parent) {
@@ -1004,6 +1014,19 @@ public final class WhiteSpaceOptions {
 		return root;
 	}
 
+	private InnerNode createDeductionDeclTree(Map workingValues, InnerNode parent) {
+		final InnerNode root = new InnerNode(parent, workingValues,
+				FormatterMessages.WhiteSpaceTabPage_deduction_guide_declarations);
+
+		createOption(root, workingValues, FormatterMessages.WhiteSpaceTabPage_deduction_guide_before_arrow,
+				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_DEDUCTION_GUIDE_ARROW,
+				DEDUCTION_GUIDE_PREVIEW);
+		createOption(root, workingValues, FormatterMessages.WhiteSpaceTabPage_deduction_guide_after_arrow,
+				DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_DEDUCTION_GUIDE_ARROW,
+				DEDUCTION_GUIDE_PREVIEW);
+		return root;
+	}
+
 	private InnerNode createLambdaDeclTree(Map workingValues, InnerNode parent) {
 		final InnerNode root = new InnerNode(parent, workingValues,
 				FormatterMessages.WhiteSpaceTabPage_lambda_expressions);