From f24acfee0fc499252fc9490741b12ed934f6962b Mon Sep 17 00:00:00 2001 From: Alireza Habibi Date: Wed, 5 May 2021 19:21:52 +0430 Subject: [PATCH] Implement extended property patterns (#52139) --- eng/targets/Settings.props | 3 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 39 +- .../Portable/Binder/DecisionDagBuilder.cs | 59 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 +- .../Portable/BoundTree/BoundSubpattern.cs | 32 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 3 + .../CSharp/Portable/Errors/MessageID.cs | 7 + .../FlowAnalysis/NullableWalker_Patterns.cs | 14 +- .../Generated/BoundNodes.xml.Generated.cs | 21 +- .../Portable/Generated/CSharp.Generated.g4 | 11 +- .../Syntax.xml.Internal.Generated.cs | 238 ++++- .../Syntax.xml.Main.Generated.cs | 31 +- .../Syntax.xml.Syntax.Generated.cs | 85 +- .../Operations/CSharpOperationFactory.cs | 1 + .../Parser/LanguageParser_Patterns.cs | 31 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 4 + .../Syntax/InternalSyntax/NameColonSyntax.cs | 11 + .../Syntax/InternalSyntax/SubpatternSyntax.cs | 14 + .../CSharp/Portable/Syntax/NameColonSyntax.cs | 32 + .../Portable/Syntax/SubpatternSyntax.cs | 28 + .../CSharp/Portable/Syntax/Syntax.xml | 18 +- .../CSharp/Portable/Syntax/SyntaxKind.cs | 3 + .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Semantics/PatternMatchingTests5.cs | 359 ++++++++ .../Generated/Syntax.Test.xml.Generated.cs | 96 +- .../Syntax/Parsing/PatternParsingTests2.cs | 869 ++++++++++++++++++ src/Compilers/Test/Core/TestResource.resx | 1 + .../Test/Utilities/CSharp/TestOptions.cs | 1 + .../PropertySubPatternCompletionProvider.cs | 1 + ...CSharpTypeInferenceService.TypeInferrer.cs | 2 + 43 files changed, 2027 insertions(+), 125 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundSubpattern.cs create mode 100644 src/Compilers/CSharp/Portable/Syntax/InternalSyntax/NameColonSyntax.cs create mode 100644 src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs create mode 100644 src/Compilers/CSharp/Portable/Syntax/NameColonSyntax.cs create mode 100644 src/Compilers/CSharp/Portable/Syntax/SubpatternSyntax.cs create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs create mode 100644 src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index ddb10ff6c6988..3e2a402241ce2 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -207,7 +207,8 @@ and hence suppress this warning until we get closer to release and a more thorough documentation story --> - $(NoWarn);1573;1591;1701 + + $(NoWarn);1573;1591;1701;RS0016 $(DefineConstants);$(InitialDefineConstants) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index b9f904f4fdea4..61402bf756da5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -780,6 +780,7 @@ deconstructMethod is null && bool isError = hasErrors || outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length; TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type; ParameterSymbol? parameter = null; + // PROTOTYPE(extended-property-patterns) ExpressionColon if (subPattern.NameColon != null && !isError) { // Check that the given name is the same as the corresponding parameter of the method. @@ -818,6 +819,7 @@ private void BindITupleSubpatterns( var objectType = Compilation.GetSpecialType(SpecialType.System_Object); foreach (var subpatternSyntax in node.Subpatterns) { + // PROTOTYPE(extended-property-patterns) ExpressionColon if (subpatternSyntax.NameColon != null) { // error: name not permitted in ITuple deconstruction @@ -874,6 +876,7 @@ private void BindValueTupleSubpatterns( bool isError = i >= elementTypesWithAnnotations.Length; TypeSymbol elementType = isError ? CreateErrorType() : elementTypesWithAnnotations[i].Type; FieldSymbol? foundField = null; + // PROTOTYPE(extended-property-patterns) ExpressionColon if (subpatternSyntax.NameColon != null && !isError) { string name = subpatternSyntax.NameColon.Name.Identifier.ValueText; @@ -1163,34 +1166,52 @@ private ImmutableArray BindPropertyPatternClause( var builder = ArrayBuilder.GetInstance(node.Subpatterns.Count); foreach (SubpatternSyntax p in node.Subpatterns) { - IdentifierNameSyntax? name = p.NameColon?.Name; + ExpressionSyntax? expr = p.ExpressionColon?.Expression; PatternSyntax pattern = p.Pattern; - Symbol? member = null; + ImmutableArray members; TypeSymbol memberType; - if (name == null) + if (expr == null) { if (!hasErrors) diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern); memberType = CreateErrorType(); + members = ImmutableArray.Empty; hasErrors = true; } else { - member = LookupMemberForPropertyPattern(inputType, name, diagnostics, ref hasErrors, out memberType); + var memberBuilder = ArrayBuilder.GetInstance(); + LookupMembersForPropertyPattern(inputType, expr, memberBuilder, diagnostics, ref hasErrors, out memberType); + members = memberBuilder.ToImmutableAndFree(); } BoundPattern boundPattern = BindPattern(pattern, memberType, GetValEscape(memberType, inputValEscape), permitDesignations, hasErrors, diagnostics); - builder.Add(new BoundSubpattern(p, member, boundPattern)); + builder.Add(new BoundSubpattern(p, members, boundPattern)); } return builder.ToImmutableAndFree(); } - private Symbol? LookupMemberForPropertyPattern( - TypeSymbol inputType, IdentifierNameSyntax name, BindingDiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType) + private void LookupMembersForPropertyPattern( + TypeSymbol inputType, ExpressionSyntax expr, ArrayBuilder builder, BindingDiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType) { - Symbol? symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics); + Symbol? symbol; + switch (expr) + { + case IdentifierNameSyntax name: + symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics); + break; + case MemberAccessExpressionSyntax { Name: IdentifierNameSyntax name } memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression): + LookupMembersForPropertyPattern(inputType, memberAccess.Expression, builder, diagnostics, ref hasErrors, out memberType); + symbol = BindPropertyPatternMember(memberType.StrippedType(), name, ref hasErrors, diagnostics); + break; + default: + Error(diagnostics, ErrorCode.ERR_InvalidNameInSubpattern, expr); + symbol = null; + hasErrors = true; + break; + } memberType = symbol switch { @@ -1199,7 +1220,7 @@ private ImmutableArray BindPropertyPatternClause( _ => CreateErrorType() }; - return symbol; + builder.AddIfNotNull(symbol); } private Symbol? BindPropertyPatternMember( diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index 9f9f94b24a6ff..b10b6628a1fae 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -527,29 +527,46 @@ private Tests MakeTestsAndBindingsForRecursivePattern( if (!recursive.Properties.IsDefault) { // we have a "property" form - for (int i = 0; i < recursive.Properties.Length; i++) + foreach (var subpattern in recursive.Properties) { - var subPattern = recursive.Properties[i]; - Symbol? symbol = subPattern.Symbol; - BoundPattern pattern = subPattern.Pattern; - BoundDagEvaluation evaluation; - switch (symbol) + BoundPattern pattern = subpattern.Pattern; + BoundDagTemp currentInput = input; + if (subpattern.Symbols.IsEmpty) { - case PropertySymbol property: - evaluation = new BoundDagPropertyEvaluation(pattern.Syntax, property, OriginalInput(input, property)); - break; - case FieldSymbol field: - evaluation = new BoundDagFieldEvaluation(pattern.Syntax, field, OriginalInput(input, field)); + addErrorTest(); + goto done; + } + + for (int index = 0, count = subpattern.Symbols.Length; ;) + { + Symbol symbol = subpattern.Symbols[index]; + BoundDagEvaluation evaluation; + switch (symbol) + { + case PropertySymbol property: + evaluation = new BoundDagPropertyEvaluation(pattern.Syntax, property, OriginalInput(currentInput, property)); + break; + case FieldSymbol field: + evaluation = new BoundDagFieldEvaluation(pattern.Syntax, field, OriginalInput(currentInput, field)); + break; + default: + addErrorTest(); + goto done; + } + + tests.Add(new Tests.One(evaluation)); + TypeSymbol type = symbol.GetTypeOrReturnType().Type; + currentInput = new BoundDagTemp(pattern.Syntax, type, evaluation); + + if (++index == count) break; - default: - RoslynDebug.Assert(recursive.HasAnyErrors); - tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true))); - continue; + + // If this is not the last member, add null test, unwrap nullables, and continue. + currentInput = MakeConvertToType(currentInput, pattern.Syntax, type.StrippedType(), isExplicitTest: false, tests); } - tests.Add(new Tests.One(evaluation)); - var element = new BoundDagTemp(pattern.Syntax, symbol.GetTypeOrReturnType().Type, evaluation); - tests.Add(MakeTestsAndBindings(element, pattern, bindings)); +done: + tests.Add(MakeTestsAndBindings(currentInput, pattern, bindings)); } } @@ -560,6 +577,12 @@ private Tests MakeTestsAndBindingsForRecursivePattern( } return Tests.AndSequence.Create(tests); + + void addErrorTest() + { + Debug.Assert(recursive.HasAnyErrors); + tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true))); + } } private Tests MakeTestsAndBindingsForNegatedPattern(BoundDagTemp input, BoundNegatedPattern neg, ArrayBuilder bindings) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index ffbc96e848323..c5c5fcb26ae6e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -2092,7 +2092,7 @@ - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundSubpattern.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundSubpattern.cs new file mode 100644 index 0000000000000..d6857aa14e23c --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundSubpattern.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Collections.Immutable; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + // PROTOTYPE(extended-property-patterns) Split BoundPropertySubpattern and remove this (requires IOperation changes) + partial class BoundSubpattern + { + internal BoundSubpattern(SyntaxNode syntax, Symbol? symbol, BoundPattern pattern, bool hasErrors = false) + : this(syntax, symbol is null ? ImmutableArray.Empty : ImmutableArray.Create(symbol), pattern, hasErrors) + { + } + + internal Symbol? Symbol + { + get + { + if (this.Symbols.IsEmpty) + return null; + if (this.Symbols.Length == 1) + return this.Symbols[0]; + return null; + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 02642221c6d9a..566733bece3c6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6609,4 +6609,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The positional member '{0}' found corresponding to this parameter is hidden. + + Identifier or a simple member access expected. + + + extended property patterns + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index c0d15cec19080..675213b62b77d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1934,6 +1934,9 @@ internal enum ErrorCode #region diagnostics introduced for C# 10.0 + // PROTOTYPE(extended-property-patterns) Temp values + ERR_InvalidNameInSubpattern = 9000, + ERR_InheritingFromRecordWithSealedToString = 8912, ERR_HiddenPositionalMember = 8913, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index a999ea580e0de..839aeebe712c7 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -103,6 +103,9 @@ internal enum MessageID IDS_FeatureDiscards = MessageBase + 12688, + // PROTOTYPE(extended-property-patterns) Move + IDS_FeatureExtendedPropertyPatterns = MessageBase + 12800, + IDS_FeatureDefaultTypeParameterConstraint = MessageBase + 12689, IDS_FeatureNullPropagatingOperator = MessageBase + 12690, IDS_FeatureExpressionBodiedMethod = MessageBase + 12691, @@ -485,6 +488,10 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureSwitchOnBool: // Checked in the binder. return LanguageVersion.CSharp2; + // PROTOTYPE(extended-property-patterns) Move + case MessageID.IDS_FeatureExtendedPropertyPatterns: + return LanguageVersion.Preview; + // Special C# 2 feature: only a warning in C# 1. case MessageID.IDS_FeatureModuleAttrLoc: return LanguageVersion.CSharp1; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index 8dfa376a19920..500c15ff2268c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -165,13 +165,17 @@ private void LearnFromAnyNullPatterns( // for property part if (!rp.Properties.IsDefault) { - for (int i = 0, n = rp.Properties.Length; i < n; i++) + foreach (BoundSubpattern subpattern in rp.Properties) { - BoundSubpattern item = rp.Properties[i]; - Symbol symbol = item.Symbol; - if (symbol?.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions) == true) + if (subpattern.Symbols.IsEmpty) + continue; + + // Obtain the last symbol's slot which is actually the one being matched. + Symbol last = subpattern.Symbols.Last(); + // PROTOTYPE(extended-property-patterns) For a chain of members, the input type is no longer `inputType` + if (last.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions)) { - LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), symbol.GetTypeOrReturnType().Type, item.Pattern); + LearnFromAnyNullPatterns(GetOrCreateSlot(last, inputSlot), last.GetTypeOrReturnType().Type, subpattern.Pattern); } } } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index d7da9cca73740..38fd845d89d3d 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -7538,28 +7538,29 @@ public BoundITuplePattern Update(MethodSymbol getLengthMethod, MethodSymbol getI internal sealed partial class BoundSubpattern : BoundNode { - public BoundSubpattern(SyntaxNode syntax, Symbol? symbol, BoundPattern pattern, bool hasErrors = false) + public BoundSubpattern(SyntaxNode syntax, ImmutableArray symbols, BoundPattern pattern, bool hasErrors = false) : base(BoundKind.Subpattern, syntax, hasErrors || pattern.HasErrors()) { + RoslynDebug.Assert(!symbols.IsDefault, "Field 'symbols' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(pattern is object, "Field 'pattern' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - this.Symbol = symbol; + this.Symbols = symbols; this.Pattern = pattern; } - public Symbol? Symbol { get; } + public ImmutableArray Symbols { get; } public BoundPattern Pattern { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSubpattern(this); - public BoundSubpattern Update(Symbol? symbol, BoundPattern pattern) + public BoundSubpattern Update(ImmutableArray symbols, BoundPattern pattern) { - if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(symbol, this.Symbol) || pattern != this.Pattern) + if (symbols != this.Symbols || pattern != this.Pattern) { - var result = new BoundSubpattern(this.Syntax, symbol, pattern, this.HasErrors); + var result = new BoundSubpattern(this.Syntax, symbols, pattern, this.HasErrors); result.CopyAttributes(this); return result; } @@ -10860,7 +10861,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitSubpattern(BoundSubpattern node) { BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); - return node.Update(node.Symbol, pattern); + return node.Update(node.Symbols, pattern); } public override BoundNode? VisitTypePattern(BoundTypePattern node) { @@ -13170,9 +13171,9 @@ public NullabilityRewriter(ImmutableDictionary symbols = GetUpdatedArray(node, node.Symbols); BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); - return node.Update(symbol, pattern); + return node.Update(symbols, pattern); } public override BoundNode? VisitTypePattern(BoundTypePattern node) @@ -15076,7 +15077,7 @@ private BoundTreeDumperNodeProducer() ); public override TreeDumperNode VisitSubpattern(BoundSubpattern node, object? arg) => new TreeDumperNode("subpattern", null, new TreeDumperNode[] { - new TreeDumperNode("symbol", node.Symbol, null), + new TreeDumperNode("symbols", node.Symbols, null), new TreeDumperNode("pattern", null, new TreeDumperNode[] { Visit(node.Pattern, null) }), new TreeDumperNode("hasErrors", node.HasErrors, null) } diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index e40f3eaedb827..6b4595a1e787b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -602,7 +602,16 @@ positional_pattern_clause ; subpattern - : name_colon? pattern + : base_expression_colon? pattern + ; + +base_expression_colon + : expression_colon + | name_colon + ; + +expression_colon + : expression ':' ; property_pattern_clause diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 7a4108972ee84..2a86e41d44351 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -5674,8 +5674,130 @@ static ArgumentSyntax() } } + internal abstract partial class BaseExpressionColonSyntax : CSharpSyntaxNode + { + internal BaseExpressionColonSyntax(SyntaxKind kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + } + + internal BaseExpressionColonSyntax(SyntaxKind kind) + : base(kind) + { + } + + protected BaseExpressionColonSyntax(ObjectReader reader) + : base(reader) + { + } + + public abstract ExpressionSyntax Expression { get; } + + public abstract SyntaxToken ColonToken { get; } + } + + internal sealed partial class ExpressionColonSyntax : BaseExpressionColonSyntax + { + internal readonly ExpressionSyntax expression; + internal readonly SyntaxToken colonToken; + + internal ExpressionColonSyntax(SyntaxKind kind, ExpressionSyntax expression, SyntaxToken colonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 2; + this.AdjustFlagsAndWidth(expression); + this.expression = expression; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + } + + internal ExpressionColonSyntax(SyntaxKind kind, ExpressionSyntax expression, SyntaxToken colonToken, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 2; + this.AdjustFlagsAndWidth(expression); + this.expression = expression; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + } + + internal ExpressionColonSyntax(SyntaxKind kind, ExpressionSyntax expression, SyntaxToken colonToken) + : base(kind) + { + this.SlotCount = 2; + this.AdjustFlagsAndWidth(expression); + this.expression = expression; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + } + + public override ExpressionSyntax Expression => this.expression; + public override SyntaxToken ColonToken => this.colonToken; + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.expression, + 1 => this.colonToken, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.ExpressionColonSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExpressionColon(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExpressionColon(this); + + public ExpressionColonSyntax Update(ExpressionSyntax expression, SyntaxToken colonToken) + { + if (expression != this.Expression || colonToken != this.ColonToken) + { + var newNode = SyntaxFactory.ExpressionColon(expression, colonToken); + var diags = GetDiagnostics(); + if (diags?.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations?.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) + => new ExpressionColonSyntax(this.Kind, this.expression, this.colonToken, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new ExpressionColonSyntax(this.Kind, this.expression, this.colonToken, GetDiagnostics(), annotations); + + internal ExpressionColonSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 2; + var expression = (ExpressionSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(expression); + this.expression = expression; + var colonToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.expression); + writer.WriteValue(this.colonToken); + } + + static ExpressionColonSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(ExpressionColonSyntax), r => new ExpressionColonSyntax(r)); + } + } + /// Class which represents the syntax node for name colon syntax. - internal sealed partial class NameColonSyntax : CSharpSyntaxNode + internal sealed partial class NameColonSyntax : BaseExpressionColonSyntax { internal readonly IdentifierNameSyntax name; internal readonly SyntaxToken colonToken; @@ -5714,7 +5836,7 @@ internal NameColonSyntax(SyntaxKind kind, IdentifierNameSyntax name, SyntaxToken /// IdentifierNameSyntax representing the identifier name. public IdentifierNameSyntax Name => this.name; /// SyntaxToken representing colon. - public SyntaxToken ColonToken => this.colonToken; + public override SyntaxToken ColonToken => this.colonToken; internal override GreenNode? GetSlot(int index) => index switch @@ -10882,56 +11004,56 @@ static PropertyPatternClauseSyntax() internal sealed partial class SubpatternSyntax : CSharpSyntaxNode { - internal readonly NameColonSyntax? nameColon; + internal readonly BaseExpressionColonSyntax? expressionColon; internal readonly PatternSyntax pattern; - internal SubpatternSyntax(SyntaxKind kind, NameColonSyntax? nameColon, PatternSyntax pattern, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + internal SubpatternSyntax(SyntaxKind kind, BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { this.SlotCount = 2; - if (nameColon != null) + if (expressionColon != null) { - this.AdjustFlagsAndWidth(nameColon); - this.nameColon = nameColon; + this.AdjustFlagsAndWidth(expressionColon); + this.expressionColon = expressionColon; } this.AdjustFlagsAndWidth(pattern); this.pattern = pattern; } - internal SubpatternSyntax(SyntaxKind kind, NameColonSyntax? nameColon, PatternSyntax pattern, SyntaxFactoryContext context) + internal SubpatternSyntax(SyntaxKind kind, BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern, SyntaxFactoryContext context) : base(kind) { this.SetFactoryContext(context); this.SlotCount = 2; - if (nameColon != null) + if (expressionColon != null) { - this.AdjustFlagsAndWidth(nameColon); - this.nameColon = nameColon; + this.AdjustFlagsAndWidth(expressionColon); + this.expressionColon = expressionColon; } this.AdjustFlagsAndWidth(pattern); this.pattern = pattern; } - internal SubpatternSyntax(SyntaxKind kind, NameColonSyntax? nameColon, PatternSyntax pattern) + internal SubpatternSyntax(SyntaxKind kind, BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern) : base(kind) { this.SlotCount = 2; - if (nameColon != null) + if (expressionColon != null) { - this.AdjustFlagsAndWidth(nameColon); - this.nameColon = nameColon; + this.AdjustFlagsAndWidth(expressionColon); + this.expressionColon = expressionColon; } this.AdjustFlagsAndWidth(pattern); this.pattern = pattern; } - public NameColonSyntax? NameColon => this.nameColon; + public BaseExpressionColonSyntax? ExpressionColon => this.expressionColon; public PatternSyntax Pattern => this.pattern; internal override GreenNode? GetSlot(int index) => index switch { - 0 => this.nameColon, + 0 => this.expressionColon, 1 => this.pattern, _ => null, }; @@ -10941,11 +11063,11 @@ internal SubpatternSyntax(SyntaxKind kind, NameColonSyntax? nameColon, PatternSy public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSubpattern(this); public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSubpattern(this); - public SubpatternSyntax Update(NameColonSyntax nameColon, PatternSyntax pattern) + public SubpatternSyntax Update(BaseExpressionColonSyntax expressionColon, PatternSyntax pattern) { - if (nameColon != this.NameColon || pattern != this.Pattern) + if (expressionColon != this.ExpressionColon || pattern != this.Pattern) { - var newNode = SyntaxFactory.Subpattern(nameColon, pattern); + var newNode = SyntaxFactory.Subpattern(expressionColon, pattern); var diags = GetDiagnostics(); if (diags?.Length > 0) newNode = newNode.WithDiagnosticsGreen(diags); @@ -10959,20 +11081,20 @@ public SubpatternSyntax Update(NameColonSyntax nameColon, PatternSyntax pattern) } internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) - => new SubpatternSyntax(this.Kind, this.nameColon, this.pattern, diagnostics, GetAnnotations()); + => new SubpatternSyntax(this.Kind, this.expressionColon, this.pattern, diagnostics, GetAnnotations()); internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) - => new SubpatternSyntax(this.Kind, this.nameColon, this.pattern, GetDiagnostics(), annotations); + => new SubpatternSyntax(this.Kind, this.expressionColon, this.pattern, GetDiagnostics(), annotations); internal SubpatternSyntax(ObjectReader reader) : base(reader) { this.SlotCount = 2; - var nameColon = (NameColonSyntax?)reader.ReadValue(); - if (nameColon != null) + var expressionColon = (BaseExpressionColonSyntax?)reader.ReadValue(); + if (expressionColon != null) { - AdjustFlagsAndWidth(nameColon); - this.nameColon = nameColon; + AdjustFlagsAndWidth(expressionColon); + this.expressionColon = expressionColon; } var pattern = (PatternSyntax)reader.ReadValue(); AdjustFlagsAndWidth(pattern); @@ -10982,7 +11104,7 @@ internal SubpatternSyntax(ObjectReader reader) internal override void WriteTo(ObjectWriter writer) { base.WriteTo(writer); - writer.WriteValue(this.nameColon); + writer.WriteValue(this.expressionColon); writer.WriteValue(this.pattern); } @@ -33004,6 +33126,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitArgumentList(ArgumentListSyntax node) => this.DefaultVisit(node); public virtual TResult VisitBracketedArgumentList(BracketedArgumentListSyntax node) => this.DefaultVisit(node); public virtual TResult VisitArgument(ArgumentSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitExpressionColon(ExpressionColonSyntax node) => this.DefaultVisit(node); public virtual TResult VisitNameColon(NameColonSyntax node) => this.DefaultVisit(node); public virtual TResult VisitDeclarationExpression(DeclarationExpressionSyntax node) => this.DefaultVisit(node); public virtual TResult VisitCastExpression(CastExpressionSyntax node) => this.DefaultVisit(node); @@ -33239,6 +33362,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitArgumentList(ArgumentListSyntax node) => this.DefaultVisit(node); public virtual void VisitBracketedArgumentList(BracketedArgumentListSyntax node) => this.DefaultVisit(node); public virtual void VisitArgument(ArgumentSyntax node) => this.DefaultVisit(node); + public virtual void VisitExpressionColon(ExpressionColonSyntax node) => this.DefaultVisit(node); public virtual void VisitNameColon(NameColonSyntax node) => this.DefaultVisit(node); public virtual void VisitDeclarationExpression(DeclarationExpressionSyntax node) => this.DefaultVisit(node); public virtual void VisitCastExpression(CastExpressionSyntax node) => this.DefaultVisit(node); @@ -33570,6 +33694,9 @@ public override CSharpSyntaxNode VisitBracketedArgumentList(BracketedArgumentLis public override CSharpSyntaxNode VisitArgument(ArgumentSyntax node) => node.Update((NameColonSyntax)Visit(node.NameColon), (SyntaxToken)Visit(node.RefKindKeyword), (ExpressionSyntax)Visit(node.Expression)); + public override CSharpSyntaxNode VisitExpressionColon(ExpressionColonSyntax node) + => node.Update((ExpressionSyntax)Visit(node.Expression), (SyntaxToken)Visit(node.ColonToken)); + public override CSharpSyntaxNode VisitNameColon(NameColonSyntax node) => node.Update((IdentifierNameSyntax)Visit(node.Name), (SyntaxToken)Visit(node.ColonToken)); @@ -33691,7 +33818,7 @@ public override CSharpSyntaxNode VisitPropertyPatternClause(PropertyPatternClaus => node.Update((SyntaxToken)Visit(node.OpenBraceToken), VisitList(node.Subpatterns), (SyntaxToken)Visit(node.CloseBraceToken)); public override CSharpSyntaxNode VisitSubpattern(SubpatternSyntax node) - => node.Update((NameColonSyntax)Visit(node.NameColon), (PatternSyntax)Visit(node.Pattern)); + => node.Update((BaseExpressionColonSyntax)Visit(node.ExpressionColon), (PatternSyntax)Visit(node.Pattern)); public override CSharpSyntaxNode VisitConstantPattern(ConstantPatternSyntax node) => node.Update((ExpressionSyntax)Visit(node.Expression)); @@ -35299,12 +35426,31 @@ public ArgumentSyntax Argument(NameColonSyntax? nameColon, SyntaxToken? refKindK return result; } + public ExpressionColonSyntax ExpressionColon(ExpressionSyntax expression, SyntaxToken colonToken) + { +#if DEBUG + if (expression == null) throw new ArgumentNullException(nameof(expression)); + if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); +#endif + + int hash; + var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.ExpressionColon, expression, colonToken, this.context, out hash); + if (cached != null) return (ExpressionColonSyntax)cached; + + var result = new ExpressionColonSyntax(SyntaxKind.ExpressionColon, expression, colonToken, this.context); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + public NameColonSyntax NameColon(IdentifierNameSyntax name, SyntaxToken colonToken) { #if DEBUG if (name == null) throw new ArgumentNullException(nameof(name)); if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); - if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); #endif int hash; @@ -36068,17 +36214,17 @@ public PropertyPatternClauseSyntax PropertyPatternClause(SyntaxToken openBraceTo return result; } - public SubpatternSyntax Subpattern(NameColonSyntax? nameColon, PatternSyntax pattern) + public SubpatternSyntax Subpattern(BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern) { #if DEBUG if (pattern == null) throw new ArgumentNullException(nameof(pattern)); #endif int hash; - var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.Subpattern, nameColon, pattern, this.context, out hash); + var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.Subpattern, expressionColon, pattern, this.context, out hash); if (cached != null) return (SubpatternSyntax)cached; - var result = new SubpatternSyntax(SyntaxKind.Subpattern, nameColon, pattern, this.context); + var result = new SubpatternSyntax(SyntaxKind.Subpattern, expressionColon, pattern, this.context); if (hash >= 0) { SyntaxNodeCache.AddNode(result, hash); @@ -40168,12 +40314,31 @@ public static ArgumentSyntax Argument(NameColonSyntax? nameColon, SyntaxToken? r return result; } + public static ExpressionColonSyntax ExpressionColon(ExpressionSyntax expression, SyntaxToken colonToken) + { +#if DEBUG + if (expression == null) throw new ArgumentNullException(nameof(expression)); + if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); +#endif + + int hash; + var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.ExpressionColon, expression, colonToken, out hash); + if (cached != null) return (ExpressionColonSyntax)cached; + + var result = new ExpressionColonSyntax(SyntaxKind.ExpressionColon, expression, colonToken); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + public static NameColonSyntax NameColon(IdentifierNameSyntax name, SyntaxToken colonToken) { #if DEBUG if (name == null) throw new ArgumentNullException(nameof(name)); if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); - if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); #endif int hash; @@ -40937,17 +41102,17 @@ public static PropertyPatternClauseSyntax PropertyPatternClause(SyntaxToken open return result; } - public static SubpatternSyntax Subpattern(NameColonSyntax? nameColon, PatternSyntax pattern) + public static SubpatternSyntax Subpattern(BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern) { #if DEBUG if (pattern == null) throw new ArgumentNullException(nameof(pattern)); #endif int hash; - var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.Subpattern, nameColon, pattern, out hash); + var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.Subpattern, expressionColon, pattern, out hash); if (cached != null) return (SubpatternSyntax)cached; - var result = new SubpatternSyntax(SyntaxKind.Subpattern, nameColon, pattern); + var result = new SubpatternSyntax(SyntaxKind.Subpattern, expressionColon, pattern); if (hash >= 0) { SyntaxNodeCache.AddNode(result, hash); @@ -43913,6 +44078,7 @@ internal static IEnumerable GetNodeTypes() typeof(ArgumentListSyntax), typeof(BracketedArgumentListSyntax), typeof(ArgumentSyntax), + typeof(ExpressionColonSyntax), typeof(NameColonSyntax), typeof(DeclarationExpressionSyntax), typeof(CastExpressionSyntax), diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index 86a0e9c3dc084..a7e66cf0c579b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -159,6 +159,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a ArgumentSyntax node. public virtual TResult? VisitArgument(ArgumentSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExpressionColonSyntax node. + public virtual TResult? VisitExpressionColon(ExpressionColonSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a NameColonSyntax node. public virtual TResult? VisitNameColon(NameColonSyntax node) => this.DefaultVisit(node); @@ -855,6 +858,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a ArgumentSyntax node. public virtual void VisitArgument(ArgumentSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExpressionColonSyntax node. + public virtual void VisitExpressionColon(ExpressionColonSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a NameColonSyntax node. public virtual void VisitNameColon(NameColonSyntax node) => this.DefaultVisit(node); @@ -1551,6 +1557,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitArgument(ArgumentSyntax node) => node.Update((NameColonSyntax?)Visit(node.NameColon), VisitToken(node.RefKindKeyword), (ExpressionSyntax?)Visit(node.Expression) ?? throw new ArgumentNullException("expression")); + public override SyntaxNode? VisitExpressionColon(ExpressionColonSyntax node) + => node.Update((ExpressionSyntax?)Visit(node.Expression) ?? throw new ArgumentNullException("expression"), VisitToken(node.ColonToken)); + public override SyntaxNode? VisitNameColon(NameColonSyntax node) => node.Update((IdentifierNameSyntax?)Visit(node.Name) ?? throw new ArgumentNullException("name"), VisitToken(node.ColonToken)); @@ -1672,7 +1681,7 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor => node.Update(VisitToken(node.OpenBraceToken), VisitList(node.Subpatterns), VisitToken(node.CloseBraceToken)); public override SyntaxNode? VisitSubpattern(SubpatternSyntax node) - => node.Update((NameColonSyntax?)Visit(node.NameColon), (PatternSyntax?)Visit(node.Pattern) ?? throw new ArgumentNullException("pattern")); + => node.Update((BaseExpressionColonSyntax?)Visit(node.ExpressionColon), (PatternSyntax?)Visit(node.Pattern) ?? throw new ArgumentNullException("pattern")); public override SyntaxNode? VisitConstantPattern(ConstantPatternSyntax node) => node.Update((ExpressionSyntax?)Visit(node.Expression) ?? throw new ArgumentNullException("expression")); @@ -3008,22 +3017,20 @@ public static ArgumentSyntax Argument(NameColonSyntax? nameColon, SyntaxToken re public static ArgumentSyntax Argument(ExpressionSyntax expression) => SyntaxFactory.Argument(default, default, expression); + /// Creates a new ExpressionColonSyntax instance. + public static ExpressionColonSyntax ExpressionColon(ExpressionSyntax expression, SyntaxToken colonToken) + { + if (expression == null) throw new ArgumentNullException(nameof(expression)); + return (ExpressionColonSyntax)Syntax.InternalSyntax.SyntaxFactory.ExpressionColon((Syntax.InternalSyntax.ExpressionSyntax)expression.Green, (Syntax.InternalSyntax.SyntaxToken)colonToken.Node!).CreateRed(); + } + /// Creates a new NameColonSyntax instance. public static NameColonSyntax NameColon(IdentifierNameSyntax name, SyntaxToken colonToken) { if (name == null) throw new ArgumentNullException(nameof(name)); - if (colonToken.Kind() != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); return (NameColonSyntax)Syntax.InternalSyntax.SyntaxFactory.NameColon((Syntax.InternalSyntax.IdentifierNameSyntax)name.Green, (Syntax.InternalSyntax.SyntaxToken)colonToken.Node!).CreateRed(); } - /// Creates a new NameColonSyntax instance. - public static NameColonSyntax NameColon(IdentifierNameSyntax name) - => SyntaxFactory.NameColon(name, SyntaxFactory.Token(SyntaxKind.ColonToken)); - - /// Creates a new NameColonSyntax instance. - public static NameColonSyntax NameColon(string name) - => SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(name), SyntaxFactory.Token(SyntaxKind.ColonToken)); - /// Creates a new DeclarationExpressionSyntax instance. public static DeclarationExpressionSyntax DeclarationExpression(TypeSyntax type, VariableDesignationSyntax designation) { @@ -3586,10 +3593,10 @@ public static PropertyPatternClauseSyntax PropertyPatternClause(SeparatedSyntaxL => SyntaxFactory.PropertyPatternClause(SyntaxFactory.Token(SyntaxKind.OpenBraceToken), subpatterns, SyntaxFactory.Token(SyntaxKind.CloseBraceToken)); /// Creates a new SubpatternSyntax instance. - public static SubpatternSyntax Subpattern(NameColonSyntax? nameColon, PatternSyntax pattern) + public static SubpatternSyntax Subpattern(BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern) { if (pattern == null) throw new ArgumentNullException(nameof(pattern)); - return (SubpatternSyntax)Syntax.InternalSyntax.SyntaxFactory.Subpattern(nameColon == null ? null : (Syntax.InternalSyntax.NameColonSyntax)nameColon.Green, (Syntax.InternalSyntax.PatternSyntax)pattern.Green).CreateRed(); + return (SubpatternSyntax)Syntax.InternalSyntax.SyntaxFactory.Subpattern(expressionColon == null ? null : (Syntax.InternalSyntax.BaseExpressionColonSyntax)expressionColon.Green, (Syntax.InternalSyntax.PatternSyntax)pattern.Green).CreateRed(); } /// Creates a new SubpatternSyntax instance. diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index 04aa76e5e9761..7e8b94dae7f05 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -2706,6 +2706,66 @@ public ArgumentSyntax Update(NameColonSyntax? nameColon, SyntaxToken refKindKeyw public ArgumentSyntax WithExpression(ExpressionSyntax expression) => Update(this.NameColon, this.RefKindKeyword, expression); } + public abstract partial class BaseExpressionColonSyntax : CSharpSyntaxNode + { + internal BaseExpressionColonSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public abstract ExpressionSyntax Expression { get; } + public BaseExpressionColonSyntax WithExpression(ExpressionSyntax expression) => WithExpressionCore(expression); + internal abstract BaseExpressionColonSyntax WithExpressionCore(ExpressionSyntax expression); + + public abstract SyntaxToken ColonToken { get; } + public BaseExpressionColonSyntax WithColonToken(SyntaxToken colonToken) => WithColonTokenCore(colonToken); + internal abstract BaseExpressionColonSyntax WithColonTokenCore(SyntaxToken colonToken); + } + + /// + /// This node is associated with the following syntax kinds: + /// + /// + /// + /// + public sealed partial class ExpressionColonSyntax : BaseExpressionColonSyntax + { + private ExpressionSyntax? expression; + + internal ExpressionColonSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override ExpressionSyntax Expression => GetRedAtZero(ref this.expression)!; + + public override SyntaxToken ColonToken => new SyntaxToken(this, ((Syntax.InternalSyntax.ExpressionColonSyntax)this.Green).colonToken, GetChildPosition(1), GetChildIndex(1)); + + internal override SyntaxNode? GetNodeSlot(int index) => index == 0 ? GetRedAtZero(ref this.expression)! : null; + + internal override SyntaxNode? GetCachedSlot(int index) => index == 0 ? this.expression : null; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExpressionColon(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitExpressionColon(this); + + public ExpressionColonSyntax Update(ExpressionSyntax expression, SyntaxToken colonToken) + { + if (expression != this.Expression || colonToken != this.ColonToken) + { + var newNode = SyntaxFactory.ExpressionColon(expression, colonToken); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + internal override BaseExpressionColonSyntax WithExpressionCore(ExpressionSyntax expression) => WithExpression(expression); + public new ExpressionColonSyntax WithExpression(ExpressionSyntax expression) => Update(expression, this.ColonToken); + internal override BaseExpressionColonSyntax WithColonTokenCore(SyntaxToken colonToken) => WithColonToken(colonToken); + public new ExpressionColonSyntax WithColonToken(SyntaxToken colonToken) => Update(this.Expression, colonToken); + } + /// Class which represents the syntax node for name colon syntax. /// /// This node is associated with the following syntax kinds: @@ -2713,7 +2773,7 @@ public ArgumentSyntax Update(NameColonSyntax? nameColon, SyntaxToken refKindKeyw /// /// /// - public sealed partial class NameColonSyntax : CSharpSyntaxNode + public sealed partial class NameColonSyntax : BaseExpressionColonSyntax { private IdentifierNameSyntax? name; @@ -2726,7 +2786,7 @@ internal NameColonSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? pare public IdentifierNameSyntax Name => GetRedAtZero(ref this.name)!; /// SyntaxToken representing colon. - public SyntaxToken ColonToken => new SyntaxToken(this, ((Syntax.InternalSyntax.NameColonSyntax)this.Green).colonToken, GetChildPosition(1), GetChildIndex(1)); + public override SyntaxToken ColonToken => new SyntaxToken(this, ((Syntax.InternalSyntax.NameColonSyntax)this.Green).colonToken, GetChildPosition(1), GetChildIndex(1)); internal override SyntaxNode? GetNodeSlot(int index) => index == 0 ? GetRedAtZero(ref this.name)! : null; @@ -2748,7 +2808,8 @@ public NameColonSyntax Update(IdentifierNameSyntax name, SyntaxToken colonToken) } public NameColonSyntax WithName(IdentifierNameSyntax name) => Update(name, this.ColonToken); - public NameColonSyntax WithColonToken(SyntaxToken colonToken) => Update(this.Name, colonToken); + internal override BaseExpressionColonSyntax WithColonTokenCore(SyntaxToken colonToken) => WithColonToken(colonToken); + public new NameColonSyntax WithColonToken(SyntaxToken colonToken) => Update(this.Name, colonToken); } /// Class which represents the syntax node for the variable declaration in an out var declaration or a deconstruction declaration. @@ -5199,7 +5260,7 @@ public PropertyPatternClauseSyntax Update(SyntaxToken openBraceToken, SeparatedS /// public sealed partial class SubpatternSyntax : CSharpSyntaxNode { - private NameColonSyntax? nameColon; + private BaseExpressionColonSyntax? expressionColon; private PatternSyntax? pattern; internal SubpatternSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) @@ -5207,14 +5268,14 @@ internal SubpatternSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? par { } - public NameColonSyntax? NameColon => GetRedAtZero(ref this.nameColon); + public BaseExpressionColonSyntax? ExpressionColon => GetRedAtZero(ref this.expressionColon); public PatternSyntax Pattern => GetRed(ref this.pattern, 1)!; internal override SyntaxNode? GetNodeSlot(int index) => index switch { - 0 => GetRedAtZero(ref this.nameColon), + 0 => GetRedAtZero(ref this.expressionColon), 1 => GetRed(ref this.pattern, 1)!, _ => null, }; @@ -5222,7 +5283,7 @@ internal SubpatternSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? par internal override SyntaxNode? GetCachedSlot(int index) => index switch { - 0 => this.nameColon, + 0 => this.expressionColon, 1 => this.pattern, _ => null, }; @@ -5230,11 +5291,11 @@ internal SubpatternSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? par public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSubpattern(this); public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitSubpattern(this); - public SubpatternSyntax Update(NameColonSyntax? nameColon, PatternSyntax pattern) + public SubpatternSyntax Update(BaseExpressionColonSyntax? expressionColon, PatternSyntax pattern) { - if (nameColon != this.NameColon || pattern != this.Pattern) + if (expressionColon != this.ExpressionColon || pattern != this.Pattern) { - var newNode = SyntaxFactory.Subpattern(nameColon, pattern); + var newNode = SyntaxFactory.Subpattern(expressionColon, pattern); var annotations = GetAnnotations(); return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; } @@ -5242,8 +5303,8 @@ public SubpatternSyntax Update(NameColonSyntax? nameColon, PatternSyntax pattern return this; } - public SubpatternSyntax WithNameColon(NameColonSyntax? nameColon) => Update(nameColon, this.Pattern); - public SubpatternSyntax WithPattern(PatternSyntax pattern) => Update(this.NameColon, pattern); + public SubpatternSyntax WithExpressionColon(BaseExpressionColonSyntax? expressionColon) => Update(expressionColon, this.Pattern); + public SubpatternSyntax WithPattern(PatternSyntax pattern) => Update(this.ExpressionColon, pattern); } /// diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 6c414ad935b8a..8e0e83546332c 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -2294,6 +2294,7 @@ internal IPropertySubpatternOperation CreatePropertySubpattern(BoundSubpattern s internal IOperation CreatePropertySubpatternMember(Symbol? symbol, ITypeSymbol matchedType, SyntaxNode syntax) { + // PROTOTYPE(extended-property-patterns) ExpressionColon var nameSyntax = (syntax is SubpatternSyntax subpatSyntax ? subpatSyntax.NameColon?.Name : null) ?? syntax; bool isImplicit = nameSyntax == syntax; switch (symbol) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 2e6b9ecae1980..7eb53d7cdb002 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -269,6 +269,10 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) case SyntaxKind.DotToken: // int.MaxValue is an expression, not a type. return false; + case SyntaxKind.MinusGreaterThanToken: + case SyntaxKind.ExclamationToken: + // parse as an expression for error recovery + return false; case var kind: // If we find what looks like a continuation of an expression, it is not a type. return !SyntaxFacts.IsBinaryExpressionOperatorToken(kind) || @@ -311,6 +315,7 @@ private PatternSyntax ParsePatternContinued(TypeSyntax type, Precedence preceden designation0 == null && subPatterns.Count == 1 && subPatterns.SeparatorCount == 0 && + // PROTOTYPE(extended-property-patterns) ExpressionColon subPatterns[0].NameColon == null) { var subpattern = subPatterns[0].Pattern; @@ -455,10 +460,15 @@ private CSharpSyntaxNode ParseExpressionOrPatternForSwitchStatement() private CSharpSyntaxNode ParseExpressionOrPatternForSwitchStatementCore() { var pattern = ParsePattern(Precedence.Conditional, whenIsKeyword: true); + return ConvertPatternToExpressionIfPossible(pattern); + } + + private CSharpSyntaxNode ConvertPatternToExpressionIfPossible(PatternSyntax pattern, bool permitTypeArguments = false) + { return pattern switch { ConstantPatternSyntax cp => cp.Expression, - TypePatternSyntax tp when ConvertTypeToExpression(tp.Type, out ExpressionSyntax expr) => expr, + TypePatternSyntax tp when ConvertTypeToExpression(tp.Type, out ExpressionSyntax expr, permitTypeArguments) => expr, DiscardPatternSyntax dp => _syntaxFactory.IdentifierName(ConvertToIdentifier(dp.UnderscoreToken)), var p => p, }; @@ -580,16 +590,21 @@ private void ParseSubpatternList( private SubpatternSyntax ParseSubpatternElement() { - NameColonSyntax nameColon = null; - if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.ColonToken) + BaseExpressionColonSyntax exprColon = null; + + PatternSyntax pattern = ParsePattern(Precedence.Conditional); + // If there is a colon but it's not preceded by a valid expression, leave it out to parse it as a missing comma, preserving C# 9.0 behavior. + if (this.CurrentToken.Kind == SyntaxKind.ColonToken && ConvertPatternToExpressionIfPossible(pattern, permitTypeArguments: true) is ExpressionSyntax expr) { - var name = this.ParseIdentifierName(); - var colon = this.EatToken(SyntaxKind.ColonToken); - nameColon = _syntaxFactory.NameColon(name, colon); + var colon = EatToken(); + exprColon = expr is IdentifierNameSyntax identifierName + ? _syntaxFactory.NameColon(identifierName, colon) + : _syntaxFactory.ExpressionColon(CheckFeatureAvailability(expr, MessageID.IDS_FeatureExtendedPropertyPatterns), colon); + + pattern = ParsePattern(Precedence.Conditional); } - var pattern = ParsePattern(Precedence.Conditional); - return this._syntaxFactory.Subpattern(nameColon, pattern); + return _syntaxFactory.Subpattern(exprColon, pattern); } /// diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 47fdde3be2e2f..cba4efabbffcc 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ +Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray +override Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/NameColonSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/NameColonSyntax.cs new file mode 100644 index 0000000000000..12218afe2b72b --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/NameColonSyntax.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax +{ + partial class NameColonSyntax + { + public override ExpressionSyntax Expression => Name; + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs new file mode 100644 index 0000000000000..71a3ed6e80468 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax +{ + partial class SubpatternSyntax + { + // PROTOTYPE(extended-property-patterns) Remove. We should always use ExpressionColon internally + public NameColonSyntax NameColon => ExpressionColon as NameColonSyntax; + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/NameColonSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/NameColonSyntax.cs new file mode 100644 index 0000000000000..04506db74c70a --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/NameColonSyntax.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Syntax +{ + partial class NameColonSyntax + { + public override ExpressionSyntax Expression => Name; + + internal override BaseExpressionColonSyntax WithExpressionCore(ExpressionSyntax expression) + { + if (expression is IdentifierNameSyntax identifierName) + return WithName(identifierName); + return SyntaxFactory.ExpressionColon(expression, this.ColonToken); + } + } +} + +namespace Microsoft.CodeAnalysis.CSharp +{ + public partial class SyntaxFactory + { + public static NameColonSyntax NameColon(IdentifierNameSyntax name) + => NameColon(name, Token(SyntaxKind.ColonToken)); + + public static NameColonSyntax NameColon(string name) + => NameColon(IdentifierName(name)); + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/SubpatternSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SubpatternSyntax.cs new file mode 100644 index 0000000000000..18644fc12cc15 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/SubpatternSyntax.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Syntax +{ + partial class SubpatternSyntax + { + public NameColonSyntax? NameColon => ExpressionColon as NameColonSyntax; + + public SubpatternSyntax WithNameColon(NameColonSyntax? nameColon) + => WithExpressionColon(nameColon); + + public SubpatternSyntax Update(NameColonSyntax? nameColon, PatternSyntax pattern) + => Update((BaseExpressionColonSyntax?)nameColon, pattern); + } +} + +namespace Microsoft.CodeAnalysis.CSharp +{ + public partial class SyntaxFactory + { + public static SubpatternSyntax Subpattern(NameColonSyntax? nameColon, PatternSyntax pattern) + => Subpattern((BaseExpressionColonSyntax?)nameColon, pattern); + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index f24059e17aa41..ffcbbaa5020b3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1254,7 +1254,18 @@ Creates an ArgumentSyntax node. - + + + + + + + + + + + + @@ -1262,8 +1273,7 @@ IdentifierNameSyntax representing the identifier name. - - + SyntaxToken representing colon. @@ -2030,7 +2040,7 @@ - + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 4686c56198157..8d2e5506d72a7 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -829,6 +829,9 @@ public enum SyntaxKind : ushort // Kinds between 9000 and 9039 are "reserved" for pattern matching. + // PROTOTYPE(extended-property-patterns) Temp value + ExpressionColon = 9039, + DeclarationExpression = 9040, RefExpression = 9050, RefType = 9051, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 43001be9164a6..543061e322dc1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Vytvoření neplatného objektu @@ -977,6 +982,11 @@ discards + + extended property patterns + extended property patterns + + target-typed object creation Vytvoření objektu s cílovým typem diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index cd35a00bd55ba..6b27fe3b2eeb9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Ungültige Objekterstellung @@ -977,6 +982,11 @@ Ausschussvariablen + + extended property patterns + extended property patterns + + target-typed object creation Objekterstellung mit Zieltyp diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 485329b2f9ed4..a2b9d9d8432e5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Creación de objeto no válida @@ -977,6 +982,11 @@ descartes + + extended property patterns + extended property patterns + + target-typed object creation creación de objetos con tipo de destino diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e97b1af9b52ba..4dde09e3f0691 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Création d'objet non valide @@ -977,6 +982,11 @@ discards (éléments ignorés) + + extended property patterns + extended property patterns + + target-typed object creation création d'un objet typé cible diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 2cd1d03b13334..a03d9e7e1c5a9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Creazione oggetto non valida @@ -977,6 +982,11 @@ rimozioni + + extended property patterns + extended property patterns + + target-typed object creation creazione di oggetti con tipo di destinazione diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 3aa0814f05e8b..087bde151e9d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation 無効なオブジェクト作成 @@ -977,6 +982,11 @@ ディスカード + + extended property patterns + extended property patterns + + target-typed object creation target-typed オブジェクトの作成 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index fe0281dc8552b..6dbb5862e4d4e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation 잘못된 개체 만들기 @@ -977,6 +982,11 @@ 무시 항목 + + extended property patterns + extended property patterns + + target-typed object creation 대상으로 형식화된 개체 만들기 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index adf1020f16365..9bafb99f48a42 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Nieprawidłowa operacja tworzenia obiektu @@ -977,6 +982,11 @@ odrzucenia + + extended property patterns + extended property patterns + + target-typed object creation tworzenie obiektu z typem docelowym diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 19c8ee7d788ba..903dd62498ffd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Criação de objeto inválido @@ -977,6 +982,11 @@ descarte + + extended property patterns + extended property patterns + + target-typed object creation criação de objeto de tipo de destino diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index aa30cff2cb48c..6f6771f32bae1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Недопустимое создание объекта @@ -977,6 +982,11 @@ пустые переменные + + extended property patterns + extended property patterns + + target-typed object creation создание объекта с типом целевого объекта diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 1501d5d81807a..2ecf010389334 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation Geçersiz nesne oluşturma @@ -977,6 +982,11 @@ atılabilir değişkenler + + extended property patterns + extended property patterns + + target-typed object creation hedeflenen türde nesne oluşturma diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index eece97e2f0267..8b51831c00117 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation 对象创建无效 @@ -977,6 +982,11 @@ 弃元 + + extended property patterns + extended property patterns + + target-typed object creation 创建目标类型对象 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 861fae90bc9b3..7aab198020b82 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -532,6 +532,11 @@ The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. + + Identifier or a simple member access expected. + Identifier or a simple member access expected. + + Invalid object creation 無效的物件建立 @@ -977,6 +982,11 @@ Discard + + extended property patterns + extended property patterns + + target-typed object creation 建立具目標類型的物件 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs new file mode 100644 index 0000000000000..a8ae361f82b63 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs @@ -0,0 +1,359 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class PatternMatchingTests5 : PatternMatchingTestBase + { + [Fact] + public void ExtendedPropertyPatterns_01() + { + var program = @" +using System; +class C +{ + public C Prop1 { get; set; } + public C Prop2 { get; set; } + public C Prop3 { get; set; } + + static bool Test1(C o) => o is { Prop1.Prop2.Prop3: null }; + static bool Test2(S o) => o is { Prop1.Prop2.Prop3: null }; + static bool Test3(S? o) => o is { Prop1.Prop2.Prop3: null }; + static bool Test4(S0 o) => o is { Prop1.Prop2.Prop3: 420 }; + + public static void Main() + { + Console.WriteLine(Test1(new() { Prop1 = new() { Prop2 = new() { Prop3 = null }}})); + Console.WriteLine(Test2(new() { Prop1 = new() { Prop2 = new() { Prop3 = null }}})); + Console.WriteLine(Test3(new() { Prop1 = new() { Prop2 = new() { Prop3 = null }}})); + + Console.WriteLine(Test1(new() { Prop1 = new() { Prop2 = null }})); + Console.WriteLine(Test2(new() { Prop1 = new() { Prop2 = null }})); + Console.WriteLine(Test3(new() { Prop1 = new() { Prop2 = null }})); + + Console.WriteLine(Test1(new() { Prop1 = null })); + Console.WriteLine(Test2(new() { Prop1 = null })); + Console.WriteLine(Test3(new() { Prop1 = null })); + + Console.WriteLine(Test1(default)); + Console.WriteLine(Test2(default)); + Console.WriteLine(Test3(default)); + + Console.WriteLine(Test4(new() { Prop1 = new() { Prop2 = new() { Prop3 = 421 }}})); + Console.WriteLine(Test4(new() { Prop1 = new() { Prop2 = new() { Prop3 = 420 }}})); + } +} +struct S { public A? Prop1; } +struct A { public B? Prop2; } +struct B { public int? Prop3; } + +struct S0 { public A0 Prop1; } +struct A0 { public B0 Prop2; } +struct B0 { public int Prop3; } +"; + var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.ReleaseExe); + compilation.VerifyDiagnostics(); + var expectedOutput = @" +True +True +True +False +False +False +False +False +False +False +False +False +False +True +"; + var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (C V_0, + C V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0021 + IL_0003: ldarg.0 + IL_0004: callvirt ""C C.Prop1.get"" + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: brfalse.s IL_0021 + IL_000d: ldloc.0 + IL_000e: callvirt ""C C.Prop2.get"" + IL_0013: stloc.1 + IL_0014: ldloc.1 + IL_0015: brfalse.s IL_0021 + IL_0017: ldloc.1 + IL_0018: callvirt ""C C.Prop3.get"" + IL_001d: ldnull + IL_001e: ceq + IL_0020: ret + IL_0021: ldc.i4.0 + IL_0022: ret +}"); + verifier.VerifyIL("C.Test2", @" +{ + // Code size 64 (0x40) + .maxstack 2 + .locals init (A? V_0, + B? V_1, + int? V_2) + IL_0000: ldarg.0 + IL_0001: ldfld ""A? S.Prop1"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""bool A?.HasValue.get"" + IL_000e: brfalse.s IL_003e + IL_0010: ldloca.s V_0 + IL_0012: call ""A A?.GetValueOrDefault()"" + IL_0017: ldfld ""B? A.Prop2"" + IL_001c: stloc.1 + IL_001d: ldloca.s V_1 + IL_001f: call ""bool B?.HasValue.get"" + IL_0024: brfalse.s IL_003e + IL_0026: ldloca.s V_1 + IL_0028: call ""B B?.GetValueOrDefault()"" + IL_002d: ldfld ""int? B.Prop3"" + IL_0032: stloc.2 + IL_0033: ldloca.s V_2 + IL_0035: call ""bool int?.HasValue.get"" + IL_003a: ldc.i4.0 + IL_003b: ceq + IL_003d: ret + IL_003e: ldc.i4.0 + IL_003f: ret +}"); + verifier.VerifyIL("C.Test4", @" +{ + // Code size 24 (0x18) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""A0 S0.Prop1"" + IL_0006: ldfld ""B0 A0.Prop2"" + IL_000b: ldfld ""int B0.Prop3"" + IL_0010: ldc.i4 0x1a4 + IL_0015: ceq + IL_0017: ret +}"); + } + + [Fact] + public void ExtendedPropertyPatterns_02() + { + var program = @" +class C +{ + public C Prop1 { get; set; } + public C Prop2 { get; set; } + + public static void Main() + { + _ = new C() is { Prop1: null } and { Prop1.Prop2: null }; + _ = new C() is { Prop1: null, Prop1.Prop2: null }; + } +} +"; + var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.ReleaseExe); + compilation.VerifyDiagnostics( + // (9,13): error CS8518: An expression of type 'C' can never match the provided pattern. + // _ = new C() is { Prop1: null } and { Prop1.Prop2: null }; + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new C() is { Prop1: null } and { Prop1.Prop2: null }").WithArguments("C").WithLocation(9, 13), + // (10,13): error CS8518: An expression of type 'C' can never match the provided pattern. + // _ = new C() is { Prop1: null, Prop1.Prop2: null }; + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new C() is { Prop1: null, Prop1.Prop2: null }").WithArguments("C").WithLocation(10, 13) + ); + } + + [Fact] + public void ExtendedPropertyPatterns_03() + { + var program = @" +using System; +class C +{ + C _prop1; + C _prop2; + + C Prop1 + { + get { Console.WriteLine(nameof(Prop1)); return _prop1; } + set => _prop1 = value; + } + C Prop2 + { + get { Console.WriteLine(nameof(Prop2)); return _prop2; } + set => _prop2 = value; + } + + public static void Main() + { + Test(null); + Test(new()); + Test(new() { Prop1 = new() }); + Test(new() { Prop1 = new() { Prop2 = new() } }); + } + static void Test(C o) + { + Console.WriteLine(nameof(Test)); + var result = o switch + { + {Prop1: null} => 1, + {Prop1.Prop2: null} => 2, + _ => -1, + }; + Console.WriteLine(result); + } +} +"; + var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.ReleaseExe); + compilation.VerifyDiagnostics(); + var expectedOutput = @" +Test +-1 +Test +Prop1 +1 +Test +Prop1 +Prop2 +2 +Test +Prop1 +Prop2 +-1"; + var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + verifier.VerifyIL("C.Test", @" +{ + // Code size 50 (0x32) + .maxstack 1 + .locals init (int V_0, + C V_1) + IL_0000: ldstr ""Test"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ldarg.0 + IL_000b: brfalse.s IL_0029 + IL_000d: ldarg.0 + IL_000e: callvirt ""C C.Prop1.get"" + IL_0013: stloc.1 + IL_0014: ldloc.1 + IL_0015: brfalse.s IL_0021 + IL_0017: ldloc.1 + IL_0018: callvirt ""C C.Prop2.get"" + IL_001d: brfalse.s IL_0025 + IL_001f: br.s IL_0029 + IL_0021: ldc.i4.1 + IL_0022: stloc.0 + IL_0023: br.s IL_002b + IL_0025: ldc.i4.2 + IL_0026: stloc.0 + IL_0027: br.s IL_002b + IL_0029: ldc.i4.m1 + IL_002a: stloc.0 + IL_002b: ldloc.0 + IL_002c: call ""void System.Console.WriteLine(int)"" + IL_0031: ret +}"); + } + + [Fact] + public void ExtendedPropertyPatterns_04() + { + var program = @" +class C +{ + public static void Main() + { + _ = new C() is { Prop1.Prop2: {} }; + _ = new C() is { Prop1->Prop2: {} }; + _ = new C() is { Prop1!.Prop2: {} }; + _ = new C() is { Prop1?.Prop2: {} }; + _ = new C() is { Prop1[0]: {} }; + _ = new C() is { 1: {} }; + } +} +"; + var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.ReleaseExe); + compilation.VerifyDiagnostics( + // (6,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { Prop1.Prop2: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Prop1").WithLocation(6, 26), + // (7,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { Prop1->Prop2: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Prop1->Prop2").WithLocation(7, 26), + // (8,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { Prop1!.Prop2: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Prop1!").WithLocation(8, 26), + // (9,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { Prop1?.Prop2: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Prop1?.Prop2").WithLocation(9, 26), + // (10,26): error CS8503: A property subpattern requires a reference to the property or field to be matched, e.g. '{ Name: Prop1[0] }' + // _ = new C() is { Prop1[0]: {} }; + Diagnostic(ErrorCode.ERR_PropertyPatternNameMissing, "Prop1[0]").WithArguments("Prop1[0]").WithLocation(10, 26), + // (10,26): error CS0246: The type or namespace name 'Prop1' could not be found (are you missing a using directive or an assembly reference?) + // _ = new C() is { Prop1[0]: {} }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Prop1").WithArguments("Prop1").WithLocation(10, 26), + // (10,31): error CS0270: Array size cannot be specified in a variable declaration (try initializing with a 'new' expression) + // _ = new C() is { Prop1[0]: {} }; + Diagnostic(ErrorCode.ERR_ArraySizeInDeclaration, "[0]").WithLocation(10, 31), + // (10,34): error CS1003: Syntax error, ',' expected + // _ = new C() is { Prop1[0]: {} }; + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(10, 34), + // (10,36): error CS1003: Syntax error, ',' expected + // _ = new C() is { Prop1[0]: {} }; + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",", "{").WithLocation(10, 36), + // (11,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { 1: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "1").WithLocation(11, 26)); + } + + [Fact] + public void ExtendedPropertyPatterns_05() + { + var program = @" +class C +{ + C Prop1, Prop2; + public static void Main() + { + _ = new C() is { Prop1.Prop2: {} }; + _ = new C() is { Prop1?.Prop2: {} }; + _ = new C() is { Missing: null, Prop1.Prop2: {} }; + } +} +"; + var compilation = CreateCompilation(program, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); + // PROTOTYPE(extended-property-patterns) False warning: https://github.com/dotnet/roslyn/issues/52956 + compilation.VerifyDiagnostics( + // (4,7): warning CS0169: The field 'C.Prop1' is never used + // C Prop1, Prop2; + Diagnostic(ErrorCode.WRN_UnreferencedField, "Prop1").WithArguments("C.Prop1").WithLocation(4, 7), + // (4,14): warning CS0169: The field 'C.Prop2' is never used + // C Prop1, Prop2; + Diagnostic(ErrorCode.WRN_UnreferencedField, "Prop2").WithArguments("C.Prop2").WithLocation(4, 14), + // (7,26): error CS8652: The feature 'extended property patterns' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new C() is { Prop1.Prop2: {} }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop1.Prop2").WithArguments("extended property patterns").WithLocation(7, 26), + // (8,26): error CS8652: The feature 'extended property patterns' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new C() is { Prop1?.Prop2: {} }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop1?.Prop2").WithArguments("extended property patterns").WithLocation(8, 26), + // (8,26): error CS9000: Identifier or a simple member access expected. + // _ = new C() is { Prop1?.Prop2: {} }; + Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Prop1?.Prop2").WithLocation(8, 26), + // (9,26): error CS0117: 'C' does not contain a definition for 'Missing' + // _ = new C() is { Missing: null, Prop1.Prop2: {} }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing").WithArguments("C", "Missing").WithLocation(9, 26), + // (9,41): error CS8652: The feature 'extended property patterns' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new C() is { Missing: null, Prop1.Prop2: {} }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop1.Prop2").WithArguments("extended property patterns").WithLocation(9, 41)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index fe56c3ae30b27..eb315a2d9478c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -154,8 +154,11 @@ private static Syntax.InternalSyntax.BracketedArgumentListSyntax GenerateBracket private static Syntax.InternalSyntax.ArgumentSyntax GenerateArgument() => InternalSyntaxFactory.Argument(null, null, GenerateIdentifierName()); + private static Syntax.InternalSyntax.ExpressionColonSyntax GenerateExpressionColon() + => InternalSyntaxFactory.ExpressionColon(GenerateIdentifierName(), InternalSyntaxFactory.Identifier("ColonToken")); + private static Syntax.InternalSyntax.NameColonSyntax GenerateNameColon() - => InternalSyntaxFactory.NameColon(GenerateIdentifierName(), InternalSyntaxFactory.Token(SyntaxKind.ColonToken)); + => InternalSyntaxFactory.NameColon(GenerateIdentifierName(), InternalSyntaxFactory.Identifier("ColonToken")); private static Syntax.InternalSyntax.DeclarationExpressionSyntax GenerateDeclarationExpression() => InternalSyntaxFactory.DeclarationExpression(GenerateIdentifierName(), GenerateSingleVariableDesignation()); @@ -1263,13 +1266,24 @@ public void TestArgumentFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestExpressionColonFactoryAndProperties() + { + var node = GenerateExpressionColon(); + + Assert.NotNull(node.Expression); + Assert.Equal(SyntaxKind.IdentifierToken, node.ColonToken.Kind); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestNameColonFactoryAndProperties() { var node = GenerateNameColon(); Assert.NotNull(node.Name); - Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind); + Assert.Equal(SyntaxKind.IdentifierToken, node.ColonToken.Kind); AttachAndCheckDiagnostics(node); } @@ -1754,7 +1768,7 @@ public void TestSubpatternFactoryAndProperties() { var node = GenerateSubpattern(); - Assert.Null(node.NameColon); + Assert.Null(node.ExpressionColon); Assert.NotNull(node.Pattern); AttachAndCheckDiagnostics(node); @@ -4910,6 +4924,32 @@ public void TestArgumentIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExpressionColonTokenDeleteRewriter() + { + var oldNode = GenerateExpressionColon(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExpressionColonIdentityRewriter() + { + var oldNode = GenerateExpressionColon(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestNameColonTokenDeleteRewriter() { @@ -9817,8 +9857,11 @@ private static BracketedArgumentListSyntax GenerateBracketedArgumentList() private static ArgumentSyntax GenerateArgument() => SyntaxFactory.Argument(default(NameColonSyntax), default(SyntaxToken), GenerateIdentifierName()); + private static ExpressionColonSyntax GenerateExpressionColon() + => SyntaxFactory.ExpressionColon(GenerateIdentifierName(), SyntaxFactory.Identifier("ColonToken")); + private static NameColonSyntax GenerateNameColon() - => SyntaxFactory.NameColon(GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.ColonToken)); + => SyntaxFactory.NameColon(GenerateIdentifierName(), SyntaxFactory.Identifier("ColonToken")); private static DeclarationExpressionSyntax GenerateDeclarationExpression() => SyntaxFactory.DeclarationExpression(GenerateIdentifierName(), GenerateSingleVariableDesignation()); @@ -9938,7 +9981,7 @@ private static PropertyPatternClauseSyntax GeneratePropertyPatternClause() => SyntaxFactory.PropertyPatternClause(SyntaxFactory.Token(SyntaxKind.OpenBraceToken), new SeparatedSyntaxList(), SyntaxFactory.Token(SyntaxKind.CloseBraceToken)); private static SubpatternSyntax GenerateSubpattern() - => SyntaxFactory.Subpattern(default(NameColonSyntax), GenerateDiscardPattern()); + => SyntaxFactory.Subpattern(default(BaseExpressionColonSyntax), GenerateDiscardPattern()); private static ConstantPatternSyntax GenerateConstantPattern() => SyntaxFactory.ConstantPattern(GenerateIdentifierName()); @@ -10926,13 +10969,24 @@ public void TestArgumentFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestExpressionColonFactoryAndProperties() + { + var node = GenerateExpressionColon(); + + Assert.NotNull(node.Expression); + Assert.Equal(SyntaxKind.IdentifierToken, node.ColonToken.Kind()); + var newNode = node.WithExpression(node.Expression).WithColonToken(node.ColonToken); + Assert.Equal(node, newNode); + } + [Fact] public void TestNameColonFactoryAndProperties() { var node = GenerateNameColon(); Assert.NotNull(node.Name); - Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind()); + Assert.Equal(SyntaxKind.IdentifierToken, node.ColonToken.Kind()); var newNode = node.WithName(node.Name).WithColonToken(node.ColonToken); Assert.Equal(node, newNode); } @@ -11417,9 +11471,9 @@ public void TestSubpatternFactoryAndProperties() { var node = GenerateSubpattern(); - Assert.Null(node.NameColon); + Assert.Null(node.ExpressionColon); Assert.NotNull(node.Pattern); - var newNode = node.WithNameColon(node.NameColon).WithPattern(node.Pattern); + var newNode = node.WithExpressionColon(node.ExpressionColon).WithPattern(node.Pattern); Assert.Equal(node, newNode); } @@ -14573,6 +14627,32 @@ public void TestArgumentIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExpressionColonTokenDeleteRewriter() + { + var oldNode = GenerateExpressionColon(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExpressionColonIdentityRewriter() + { + var oldNode = GenerateExpressionColon(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestNameColonTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs new file mode 100644 index 0000000000000..ee47e59f6cbde --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs @@ -0,0 +1,869 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + [CompilerTrait(CompilerFeature.Patterns)] + public class PatternParsingTests2 : ParsingTests + { + private new void UsingExpression(string text, params DiagnosticDescription[] expectedErrors) + { + UsingExpression(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), expectedErrors); + } + + public PatternParsingTests2(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ExtendedPropertySubpattern_01() + { + UsingExpression(@"e is { a.b.c: p }"); + verify(); + + UsingExpression(@"e is { a.b.c: p }", TestOptions.Regular9, + // (1,8): error CS8652: The feature 'extended property patterns' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // e is { a.b.c: p } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "a.b.c").WithArguments("extended property patterns").WithLocation(1, 8)); + verify(); + + void verify() + { + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + } + + [Fact] + public void ExtendedPropertySubpattern_02() + { + UsingExpression(@"e is { {}: p }", + // (1,10): error CS1003: Syntax error, ',' expected + // e is { {}: p } + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(1, 10), + // (1,12): error CS1003: Syntax error, ',' expected + // e is { {}: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 12)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_03() + { + UsingExpression(@"e is { name: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "name"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_04() + { + UsingExpression(@"e is { name[0]: p }", + // (1,15): error CS1003: Syntax error, ',' expected + // e is { name[0]: p } + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(1, 15), + // (1,17): error CS1003: Syntax error, ',' expected + // e is { name[0]: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 17)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "name"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_05() + { + UsingExpression(@"e is { a?.b: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_06() + { + UsingExpression(@"e is { a->c: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.PointerMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.MinusGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_07() + { + UsingExpression(@"e is { [0]: p }", + // (1,8): error CS1001: Identifier expected + // e is { [0]: p } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "[").WithLocation(1, 8), + // (1,10): error CS1003: Syntax error, ',' expected + // e is { [0]: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "]").WithArguments(",", "]").WithLocation(1, 10), + // (1,13): error CS1003: Syntax error, ',' expected + // e is { [0]: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 13)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_08() + { + UsingExpression(@"e is { not a: p }", + // (1,13): error CS1003: Syntax error, ',' expected + // e is { not a: p } + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(1, 13), + // (1,15): error CS1003: Syntax error, ',' expected + // e is { not a: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 15)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NotPattern); + { + N(SyntaxKind.NotKeyword); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_09() + { + UsingExpression(@"e is { x or y: p }", + // (1,14): error CS1003: Syntax error, ',' expected + // e is { x or y: p } + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(1, 14), + // (1,16): error CS1003: Syntax error, ',' expected + // e is { x or y: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 16)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_10() + { + UsingExpression(@"e is { 1: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_11() + { + UsingExpression(@"e is { >1: p }", + // (1,10): error CS1003: Syntax error, ',' expected + // e is { >1: p } + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",", ":").WithLocation(1, 10), + // (1,12): error CS1003: Syntax error, ',' expected + // e is { >1: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "p").WithArguments(",", "").WithLocation(1, 12)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.RelationalPattern); + { + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_12() + { + UsingExpression(@"e is { a!.b: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.SuppressNullableWarningExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ExclamationToken); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_13() + { + UsingExpression(@"e is { a[0].b: p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_14() + { + UsingExpression(@"e is { [0].b: p }", + // (1,8): error CS1001: Identifier expected + // e is { [0].b: p } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "[").WithLocation(1, 8), + // (1,10): error CS1003: Syntax error, ',' expected + // e is { [0].b: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "]").WithArguments(",", "]").WithLocation(1, 10), + // (1,12): error CS1003: Syntax error, ',' expected + // e is { [0].b: p } + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",", "").WithLocation(1, 12)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact] + public void ExtendedPropertySubpattern_15() + { + UsingExpression(@"e is { (c?a:b): p }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ExpressionColon); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + } +} diff --git a/src/Compilers/Test/Core/TestResource.resx b/src/Compilers/Test/Core/TestResource.resx index cb6d8b34f7d68..c337fa629026e 100644 --- a/src/Compilers/Test/Core/TestResource.resx +++ b/src/Compilers/Test/Core/TestResource.resx @@ -205,6 +205,7 @@ namespace My case X q: break; case var (x, y) when x == y: break; case var (x, (y, z)) when x == y: break; + case { x.y: p }: break; case _: break; } } diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index f6bcde40bc845..e5bba5b842d74 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -47,6 +47,7 @@ public static class TestOptions public static readonly CSharpParseOptions RegularWithRecursivePatterns = Regular8; public static readonly CSharpParseOptions RegularWithoutPatternCombinators = Regular8; public static readonly CSharpParseOptions RegularWithPatternCombinators = RegularPreview; + public static readonly CSharpParseOptions RegularWithExtendedPropertyPatterns = RegularPreview; public static readonly CSharpCompilationOptions ReleaseDll = CreateTestOptions(OutputKind.DynamicallyLinkedLibrary, OptimizationLevel.Release); public static readonly CSharpCompilationOptions ReleaseExe = CreateTestOptions(OutputKind.ConsoleApplication, OptimizationLevel.Release); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs index 43069af8b68ef..3bdaf42f489c9 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs @@ -66,6 +66,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) // List the members that are already tested in this property sub-pattern var alreadyTestedMembers = new HashSet(propertyPatternClause.Subpatterns.Select( + // PROTOTYPE(extended-property-patterns) ExpressionColon p => p.NameColon?.Name.Identifier.ValueText).Where(s => !string.IsNullOrEmpty(s))); var untestedMembers = members.Where(m => !alreadyTestedMembers.Contains(m.Name) && diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index 12f7f86be0161..7caa73b8e5150 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -1458,6 +1458,7 @@ private IEnumerable InferTypeInSubpattern( // parent type. So look up the parent type first, then find the X member in it // and use that type. if (child == subpattern.Pattern && + // PROTOTYPE(extended-property-patterns) ExpressionColon subpattern.NameColon != null) { var result = ArrayBuilder.GetInstance(); @@ -1558,6 +1559,7 @@ private IEnumerable GetTypesForRecursivePattern(RecursivePatt foreach (var subPattern in positionalPart.Subpatterns) { + // PROTOTYPE(extended-property-patterns) ExpressionColon elementNamesBuilder.Add(subPattern.NameColon?.Name.Identifier.ValueText); var patternType = GetPatternTypes(subPattern.Pattern).FirstOrDefault();