diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md
index 3fcf98b6fc6e8..7f0e99341223f 100644
--- a/docs/contributing/Compiler Test Plan.md
+++ b/docs/contributing/Compiler Test Plan.md
@@ -92,7 +92,7 @@ This document provides guidance for thinking about language interactions and tes
- Ref return, ref readonly return, ref ternary, ref readonly local, ref local re-assignment, ref foreach
- `this = e;` in `struct` .ctor
- Stackalloc (including initializers)
-- Patterns (constant, declaration, `var`, positional, property, discard, parenthesized, type, relational, `and`/`or`/`not`)
+- Patterns (constant, declaration, `var`, positional, property and extended property, discard, parenthesized, type, relational, `and`/`or`/`not`)
- Switch expressions
- With expressions (on record classes and on value types)
- Nullability annotations (`?`, attributes) and analysis
diff --git a/docs/features/patterns.work.md b/docs/features/patterns.work.md
index 4ac2171d51778..6fdb48217267c 100644
--- a/docs/features/patterns.work.md
+++ b/docs/features/patterns.work.md
@@ -30,7 +30,7 @@
- [ ] Test all of the ways that a name would not be valid to name a property in a property pattern
- [ ] Need to design the data representation for edit-and-continue for temps in patterns
- [ ] Need to precisely share temps per the edit-and-continue spec
- - [ ] Need bullets here or github issues for PROTOTYPE(patterns2) comments.
+ - [ ] Need bullets here or github issues for prototype comments.
- [ ] Need to ensure good code quality, e.g. avoid redundant null checks preceding types tests
- [ ] Need to ensure a good tradeoff between decision tree size explosion and execution of redundant tests.
diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props
index 446f5d0c2c735..089e0df83de0c 100644
--- a/eng/targets/Settings.props
+++ b/eng/targets/Settings.props
@@ -208,8 +208,7 @@
and hence suppress this warning until we get closer to release and a more
thorough documentation story
-->
-
- $(NoWarn);1573;1591;1701;RS0016
+ $(NoWarn);1573;1591;1701
$(DefineConstants);$(InitialDefineConstants)
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
index 4926284fec538..13d0d455f612d 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
@@ -218,10 +218,33 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern(
if (!hasErrors)
CheckFeatureAvailability(innerExpression, MessageID.IDS_FeatureTypePattern, diagnostics);
+ if (hasSuppression(expression))
+ {
+ diagnostics.Add(ErrorCode.ERR_IllegalSuppression, expression.Location);
+ hasErrors = true;
+ }
+
var boundType = (BoundTypeExpression)convertedExpression;
bool isExplicitNotNullTest = boundType.Type.SpecialType == SpecialType.System_Object;
return new BoundTypePattern(node, boundType, isExplicitNotNullTest, inputType, boundType.Type, hasErrors);
}
+
+ static bool hasSuppression(ExpressionSyntax e)
+ {
+ while (true)
+ {
+ switch (e)
+ {
+ case ParenthesizedExpressionSyntax p:
+ e = p.Expression;
+ break;
+ case PostfixUnaryExpressionSyntax { RawKind: (int)SyntaxKind.SuppressNullableWarningExpression }:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
}
private ExpressionSyntax SkipParensAndNullSuppressions(ExpressionSyntax e)
@@ -780,21 +803,27 @@ 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)
+ if (!isError)
{
- // Check that the given name is the same as the corresponding parameter of the method.
- int parameterIndex = i + skippedExtensionParameters;
- if (parameterIndex < deconstructMethod!.ParameterCount)
+ if (subPattern.NameColon != null)
{
- parameter = deconstructMethod.Parameters[parameterIndex];
- string name = subPattern.NameColon.Name.Identifier.ValueText;
- string parameterName = parameter.Name;
- if (name != parameterName)
+ // Check that the given name is the same as the corresponding parameter of the method.
+ int parameterIndex = i + skippedExtensionParameters;
+ if (parameterIndex < deconstructMethod!.ParameterCount)
{
- diagnostics.Add(ErrorCode.ERR_DeconstructParameterNameMismatch, subPattern.NameColon.Name.Location, name, parameterName);
+ parameter = deconstructMethod.Parameters[parameterIndex];
+ string name = subPattern.NameColon.Name.Identifier.ValueText;
+ string parameterName = parameter.Name;
+ if (name != parameterName)
+ {
+ diagnostics.Add(ErrorCode.ERR_DeconstructParameterNameMismatch, subPattern.NameColon.Name.Location, name, parameterName);
+ }
}
}
+ else if (subPattern.ExpressionColon != null)
+ {
+ diagnostics.Add(ErrorCode.ERR_IdentifierExpected, subPattern.ExpressionColon.Expression.Location);
+ }
}
var boundSubpattern = new BoundPositionalSubpattern(
@@ -819,12 +848,15 @@ 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
diagnostics.Add(ErrorCode.ERR_ArgumentNameInITuplePattern, subpatternSyntax.NameColon.Location);
}
+ else if (subpatternSyntax.ExpressionColon != null)
+ {
+ diagnostics.Add(ErrorCode.ERR_IdentifierExpected, subpatternSyntax.ExpressionColon.Expression.Location);
+ }
var boundSubpattern = new BoundPositionalSubpattern(
subpatternSyntax,
@@ -876,11 +908,17 @@ 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)
+ if (!isError)
{
- string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
- foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
+ if (subpatternSyntax.NameColon != null)
+ {
+ string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
+ foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
+ }
+ else if (subpatternSyntax.ExpressionColon != null)
+ {
+ diagnostics.Add(ErrorCode.ERR_IdentifierExpected, subpatternSyntax.ExpressionColon.Expression.Location);
+ }
}
BoundPositionalSubpattern boundSubpattern = new BoundPositionalSubpattern(
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
index a81c3ecccdafa..af32453b912f2 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
@@ -2035,7 +2035,6 @@ internal CSharpTypeInfo GetTypeInfoForNode(
}
if (lowestBoundNode is BoundPropertySubpatternMember member)
{
- // PROTOTYPE(extended-property-patterns) Should we capture unwrapped nullables in nested members as the ConvertedType?
return new CSharpTypeInfo(member.Type, member.Type, nullability: default, convertedNullability: default, Conversion.Identity);
}
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 93662aeb73d2e..31c3e8954ca50 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1932,15 +1932,13 @@ 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,
ERR_GlobalUsingInNamespace = 8914,
ERR_GlobalUsingOutOfOrder = 8915,
ERR_AttributesRequireParenthesizedLambdaExpression = 8916,
ERR_CannotInferDelegateType = 8917,
+ ERR_InvalidNameInSubpattern = 8918,
#endregion
diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs
index ef6d6999d4aa9..00a46eeec0a85 100644
--- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs
+++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs
@@ -339,6 +339,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureGlobalUsing:
case MessageID.IDS_FeatureInferredDelegateType: // semantic check
case MessageID.IDS_FeatureLambdaAttributes: // semantic check
+ case MessageID.IDS_FeatureExtendedPropertyPatterns:
return LanguageVersion.Preview;
// C# 9.0 features.
@@ -499,10 +500,6 @@ 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/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
index f5ce80db5ba92..47f3e34b9a04e 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
@@ -1580,87 +1580,98 @@ public override void VisitPattern(BoundPattern pattern)
base.VisitPattern(pattern);
var whenFail = StateWhenFalse;
SetState(StateWhenTrue);
- AssignPatternVariables(pattern);
+ assignPatternVariablesAndMarkReadFields(pattern);
SetConditionalState(this.State, whenFail);
- }
- ///
- /// Find the pattern variables of the pattern, and make them definitely assigned if .
- /// That would be false under "not" and "or" patterns.
- ///
- private void AssignPatternVariables(BoundPattern pattern, bool definitely = true)
- {
- switch (pattern.Kind)
+ // Find the pattern variables of the pattern, and make them definitely assigned if .
+ // That would be false under "not" and "or" patterns.
+ void assignPatternVariablesAndMarkReadFields(BoundPattern pattern, bool definitely = true)
{
- case BoundKind.DeclarationPattern:
- {
- var pat = (BoundDeclarationPattern)pattern;
- if (definitely)
- Assign(pat, value: null, isRef: false, read: false);
- break;
- }
- case BoundKind.DiscardPattern:
- break;
- case BoundKind.ConstantPattern:
- {
- var pat = (BoundConstantPattern)pattern;
- this.VisitRvalue(pat.Value);
+ switch (pattern.Kind)
+ {
+ case BoundKind.DeclarationPattern:
+ {
+ var pat = (BoundDeclarationPattern)pattern;
+ if (definitely)
+ Assign(pat, value: null, isRef: false, read: false);
+ break;
+ }
+ case BoundKind.DiscardPattern:
break;
- }
- case BoundKind.RecursivePattern:
- {
- var pat = (BoundRecursivePattern)pattern;
- if (!pat.Deconstruction.IsDefaultOrEmpty)
+ case BoundKind.ConstantPattern:
+ {
+ var pat = (BoundConstantPattern)pattern;
+ this.VisitRvalue(pat.Value);
+ break;
+ }
+ case BoundKind.RecursivePattern:
{
- foreach (var subpat in pat.Deconstruction)
+ var pat = (BoundRecursivePattern)pattern;
+ if (!pat.Deconstruction.IsDefaultOrEmpty)
{
- AssignPatternVariables(subpat.Pattern, definitely);
+ foreach (var subpat in pat.Deconstruction)
+ {
+ assignPatternVariablesAndMarkReadFields(subpat.Pattern, definitely);
+ }
}
+ if (!pat.Properties.IsDefaultOrEmpty)
+ {
+ foreach (BoundSubpattern sub in pat.Properties)
+ {
+ if (sub is BoundPropertySubpattern { Member: var member }
+ && _sourceAssembly is not null)
+ {
+ while (member is not null)
+ {
+ if (member.Symbol is FieldSymbol field)
+ {
+ _sourceAssembly.NoteFieldAccess(field, read: true, write: false);
+ }
+
+ member = member.Receiver;
+ }
+ }
+ assignPatternVariablesAndMarkReadFields(sub.Pattern, definitely);
+ }
+ }
+ if (definitely)
+ Assign(pat, null, false, false);
+ break;
}
- if (!pat.Properties.IsDefaultOrEmpty)
+ case BoundKind.ITuplePattern:
{
- foreach (BoundSubpattern sub in pat.Properties)
+ var pat = (BoundITuplePattern)pattern;
+ foreach (var subpat in pat.Subpatterns)
{
- AssignPatternVariables(sub.Pattern, definitely);
+ assignPatternVariablesAndMarkReadFields(subpat.Pattern, definitely);
}
+ break;
}
- if (definitely)
- Assign(pat, null, false, false);
+ case BoundKind.TypePattern:
break;
- }
- case BoundKind.ITuplePattern:
- {
- var pat = (BoundITuplePattern)pattern;
- foreach (var subpat in pat.Subpatterns)
+ case BoundKind.RelationalPattern:
{
- AssignPatternVariables(subpat.Pattern, definitely);
+ var pat = (BoundRelationalPattern)pattern;
+ this.VisitRvalue(pat.Value);
+ break;
}
- break;
- }
- case BoundKind.TypePattern:
- break;
- case BoundKind.RelationalPattern:
- {
- var pat = (BoundRelationalPattern)pattern;
- this.VisitRvalue(pat.Value);
- break;
- }
- case BoundKind.NegatedPattern:
- {
- var pat = (BoundNegatedPattern)pattern;
- AssignPatternVariables(pat.Negated, definitely: false);
- break;
- }
- case BoundKind.BinaryPattern:
- {
- var pat = (BoundBinaryPattern)pattern;
- bool def = definitely && !pat.Disjunction;
- AssignPatternVariables(pat.Left, def);
- AssignPatternVariables(pat.Right, def);
- break;
- }
- default:
- throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
+ case BoundKind.NegatedPattern:
+ {
+ var pat = (BoundNegatedPattern)pattern;
+ assignPatternVariablesAndMarkReadFields(pat.Negated, definitely: false);
+ break;
+ }
+ case BoundKind.BinaryPattern:
+ {
+ var pat = (BoundBinaryPattern)pattern;
+ bool def = definitely && !pat.Disjunction;
+ assignPatternVariablesAndMarkReadFields(pat.Left, def);
+ assignPatternVariablesAndMarkReadFields(pat.Right, def);
+ break;
+ }
+ default:
+ throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs
index 0c348ddf2b1d6..5c7294dd20ef5 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs
@@ -173,10 +173,9 @@ private void LearnFromAnyNullPatterns(
{
foreach (BoundPropertySubpattern subpattern in rp.Properties)
{
- // PROTOTYPE(extended-property-patterns): Investigate if we need to visit nested members; is there a test gap?
- if (subpattern.Member is { Symbol: Symbol symbol } member)
+ if (subpattern.Member is BoundPropertySubpatternMember member)
{
- LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), member.Type, subpattern.Pattern);
+ LearnFromAnyNullPatterns(getExtendedPropertySlot(member, inputSlot), member.Type, subpattern.Pattern);
}
}
}
@@ -192,6 +191,26 @@ private void LearnFromAnyNullPatterns(
default:
throw ExceptionUtilities.UnexpectedValue(pattern);
}
+
+ int getExtendedPropertySlot(BoundPropertySubpatternMember member, int inputSlot)
+ {
+ if (member.Symbol is null)
+ {
+ return -1;
+ }
+
+ if (member.Receiver is not null)
+ {
+ inputSlot = getExtendedPropertySlot(member.Receiver, inputSlot);
+ }
+
+ if (inputSlot < 0)
+ {
+ return inputSlot;
+ }
+
+ return GetOrCreateSlot(member.Symbol, inputSlot);
+ }
}
protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
index 3460030f81ddc..6d238dc3e99df 100644
--- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
+++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
@@ -2286,48 +2286,81 @@ private IOperation CreateBoundDiscardPatternOperation(BoundDiscardPattern boundN
internal IPropertySubpatternOperation CreatePropertySubpattern(BoundPropertySubpattern subpattern, ITypeSymbol matchedType)
{
- SyntaxNode syntax = subpattern.Syntax;
- IOperation member = CreatePropertySubpatternMember(subpattern.Member, matchedType) ??
- OperationFactory.CreateInvalidOperation(_semanticModel, syntax, ImmutableArray.Empty, isImplicit: true);
- IPatternOperation pattern = (IPatternOperation)Create(subpattern.Pattern);
- return new PropertySubpatternOperation(member, pattern, _semanticModel, syntax, isImplicit: false);
- }
+ // We treat `c is { ... .Prop: }` as `c is { ...: { Prop: } }`
- internal IOperation? CreatePropertySubpatternMember(BoundPropertySubpatternMember? member, ITypeSymbol matchedType)
- {
+ SyntaxNode subpatternSyntax = subpattern.Syntax;
+ BoundPropertySubpatternMember? member = subpattern.Member;
+ IPatternOperation pattern = (IPatternOperation)Create(subpattern.Pattern);
if (member is null)
- return null;
+ {
+ var reference = OperationFactory.CreateInvalidOperation(_semanticModel, subpatternSyntax, ImmutableArray.Empty, isImplicit: true);
+ return new PropertySubpatternOperation(reference, pattern, _semanticModel, subpatternSyntax, isImplicit: false);
+ }
+
+ // Create an operation for last property access:
+ // `{ SingleProp: }`
+ // or
+ // `.LastProp: ` portion (treated as `{ LastProp: }`)
+ var nameSyntax = member.Syntax;
+ var inputType = getInputType(member, matchedType);
+ IPropertySubpatternOperation? result = createPropertySubpattern(member.Symbol, pattern, inputType, nameSyntax, isSingle: member.Receiver is null);
- switch (member.Symbol)
+ while (member.Receiver is not null)
{
- case FieldSymbol field:
- {
- var constantValue = field.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
- return new FieldReferenceOperation(field.GetPublicSymbol(), isDeclaration: false,
- // PROTOTYPE(extended-property-patterns) We shouldn't use StrippedType here, maybe only if this is a nested member?
- createReceiver(), _semanticModel, member.Syntax, field.Type.StrippedType().GetPublicSymbol(), constantValue, isImplicit: false);
- }
- case PropertySymbol property:
- {
- return new PropertyReferenceOperation(property.GetPublicSymbol(), ImmutableArray.Empty,
- // PROTOTYPE(extended-property-patterns) We shouldn't use StrippedType here, maybe only if this is a nested member?
- createReceiver(), _semanticModel, member.Syntax, property.Type.StrippedType().GetPublicSymbol(), isImplicit: false);
- }
- default:
- {
- // We should expose the symbol in this case somehow:
- // https://github.com/dotnet/roslyn/issues/33175
- IOperation? receiver = CreatePropertySubpatternMember(member.Receiver, matchedType);
- var children = receiver is null ? ImmutableArray.Empty : ImmutableArray.Create(receiver);
- return OperationFactory.CreateInvalidOperation(_semanticModel, member.Syntax, children, isImplicit: false);
- }
+ member = member.Receiver;
+ nameSyntax = member.Syntax;
+ ITypeSymbol previousType = inputType;
+ inputType = getInputType(member, matchedType);
+
+ // Create an operation for a preceding property access:
+ // { PrecedingProp: }
+ IPatternOperation nestedPattern = new RecursivePatternOperation(
+ matchedType: previousType, deconstructSymbol: null, deconstructionSubpatterns: ImmutableArray.Empty,
+ propertySubpatterns: ImmutableArray.Create(result), declaredSymbol: null,
+ previousType, narrowedType: previousType, semanticModel: _semanticModel, nameSyntax, isImplicit: true);
+
+ result = createPropertySubpattern(member.Symbol, nestedPattern, inputType, nameSyntax, isSingle: false);
+ }
+
+ return result;
+
+ IPropertySubpatternOperation createPropertySubpattern(Symbol? symbol, IPatternOperation pattern, ITypeSymbol receiverType, SyntaxNode nameSyntax, bool isSingle)
+ {
+ Debug.Assert(nameSyntax is not null);
+ IOperation reference;
+ switch (symbol)
+ {
+ case FieldSymbol field:
+ {
+ var constantValue = field.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
+ reference = new FieldReferenceOperation(field.GetPublicSymbol(), isDeclaration: false,
+ createReceiver(), _semanticModel, nameSyntax, type: field.Type.GetPublicSymbol(), constantValue, isImplicit: false);
+ break;
+ }
+ case PropertySymbol property:
+ {
+ reference = new PropertyReferenceOperation(property.GetPublicSymbol(), ImmutableArray.Empty,
+ createReceiver(), _semanticModel, nameSyntax, type: property.Type.GetPublicSymbol(), isImplicit: false);
+ break;
+ }
+ default:
+ {
+ // We should expose the symbol in this case somehow:
+ // https://github.com/dotnet/roslyn/issues/33175
+ reference = OperationFactory.CreateInvalidOperation(_semanticModel, nameSyntax, ImmutableArray.Empty, isImplicit: false);
+ break;
+ }
+ }
+
+ var syntaxForPropertySubpattern = isSingle ? subpatternSyntax : nameSyntax;
+ return new PropertySubpatternOperation(reference, pattern, _semanticModel, syntaxForPropertySubpattern, isImplicit: !isSingle);
+
+ IOperation? createReceiver()
+ => symbol?.IsStatic == false ? new InstanceReferenceOperation(InstanceReferenceKind.PatternInput, _semanticModel, nameSyntax!, receiverType, isImplicit: true) : null;
}
- IOperation? createReceiver()
- => CreatePropertySubpatternMember(member.Receiver, matchedType) ??
- (member.Symbol.IsStatic == false
- ? new InstanceReferenceOperation(InstanceReferenceKind.PatternInput, _semanticModel, member.Syntax, matchedType, isImplicit: true)
- : null);
+ static ITypeSymbol getInputType(BoundPropertySubpatternMember member, ITypeSymbol matchedType)
+ => member.Receiver?.Type.StrippedType().GetPublicSymbol() ?? matchedType;
}
private IInstanceReferenceOperation CreateCollectionValuePlaceholderOperation(BoundObjectOrCollectionValuePlaceholder placeholder)
diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs
index 7eb53d7cdb002..e3966c4d923c3 100644
--- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs
+++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs
@@ -315,8 +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)
+ subPatterns[0].ExpressionColon == null)
{
var subpattern = subPatterns[0].Pattern;
switch (subpattern)
diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
index ba41992da8333..1ec8fc6c2c318 100644
--- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
+++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
@@ -1,6 +1,23 @@
+abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.ExpressionColon.get -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax expressionColon, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.WithExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax expressionColon) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax
+Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionColon = 9069 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordStructDeclaration = 9068 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray
+override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
+override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
+override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult
+override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax
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
@@ -11,6 +28,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.AddAttr
override Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.AttributeLists.get -> Microsoft.CodeAnalysis.SyntaxList
Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxToken arrowToken, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxToken arrowToken, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.WithClassOrStructKeyword(Microsoft.CodeAnalysis.SyntaxToken classOrStructKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax
@@ -26,9 +44,10 @@ Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax.Update(Microso
Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SimpleLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax parameter, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SimpleLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax parameter, Microsoft.CodeAnalysis.SyntaxToken arrowToken, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleLambdaExpressionSyntax
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Subpattern(Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax expressionColon, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.UsingDirective(Microsoft.CodeAnalysis.SyntaxToken globalKeyword, Microsoft.CodeAnalysis.SyntaxToken usingKeyword, Microsoft.CodeAnalysis.SyntaxToken staticKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameEqualsSyntax alias, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.GlobalKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken globalKeyword, Microsoft.CodeAnalysis.SyntaxToken usingKeyword, Microsoft.CodeAnalysis.SyntaxToken staticKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameEqualsSyntax alias, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.WithGlobalKeyword(Microsoft.CodeAnalysis.SyntaxToken globalKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax
-
-
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> void
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> TResult
diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs
deleted file mode 100644
index 71a3ed6e80468..0000000000000
--- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SubpatternSyntax.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
index 52f958e86acef..cc20c1ed5e6ee 100644
--- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
@@ -829,9 +829,6 @@ 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,
@@ -859,5 +856,7 @@ public enum SyntaxKind : ushort
FunctionPointerUnmanagedCallingConvention = 9067,
RecordStructDeclaration = 9068,
+
+ ExpressionColon = 9069,
}
}
diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
index 1bef6e3e02daf..7fc105788d1fe 100644
--- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
+++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
@@ -1024,7 +1024,7 @@ public void IsPattern_RecursivePatternWithNestedPropertyPatterns()
class C
{
C field;
- C prop { get; }
+ C prop { get; }
void M()
{
if (/**/this is { prop.field: null }/**/) { }
@@ -1033,31 +1033,38 @@ void M()
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'this is { p ... eld: null }')
- Value:
+ Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
- Pattern:
+ Pattern:
IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ prop.field: null }') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
DeconstructionSubpatterns (0)
PropertySubpatterns (1):
- IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null) (Syntax: 'prop.field: null')
- Member:
- IFieldReferenceOperation: C C.field (OperationKind.FieldReference, Type: C) (Syntax: 'prop.field')
- Instance Receiver:
- IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
- Instance Receiver:
- IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
- Pattern:
- IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: C, NarrowedType: C)
- Value:
- IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C, Constant: null, IsImplicit) (Syntax: 'null')
- Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
- Operand:
- ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'prop')
+ Member:
+ IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'prop') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'prop.field')
+ Member:
+ IFieldReferenceOperation: C C.field (OperationKind.FieldReference, Type: C) (Syntax: 'prop.field')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop.field')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: C, NarrowedType: C)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
";
var expectedDiagnostics = new DiagnosticDescription[] {
- // file.cs(4,7): warning CS0169: The field 'C.field' is never used
- // C field;
- Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("C.field").WithLocation(4, 7)
+ // file.cs(4,7): warning CS0649: Field 'C.field' is never assigned to, and will always have its default value null
+ // C field;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("C.field", "null").WithLocation(4, 7)
};
VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
@@ -1071,7 +1078,7 @@ public void IsPattern_RecursivePatternWithNestedPropertyPatterns_ControlFlow()
class C
{
C field;
- C prop { get; }
+ C prop { get; }
void M()
/**/
{
@@ -1088,27 +1095,34 @@ void M()
Statements (0)
Jump if False (Regular) to Block[B2]
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'this is { p ... eld: null }')
- Value:
+ Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
- Pattern:
+ Pattern:
IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ prop.field: null }') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
- DeconstructionSubpatterns (0)
- PropertySubpatterns (1):
- IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null) (Syntax: 'prop.field: null')
- Member:
- IFieldReferenceOperation: C C.field (OperationKind.FieldReference, Type: C) (Syntax: 'prop.field')
- Instance Receiver:
- IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
- Instance Receiver:
- IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
- Pattern:
- IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: C, NarrowedType: C)
- Value:
- IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C, Constant: null, IsImplicit) (Syntax: 'null')
- Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
- (ImplicitReference)
- Operand:
- ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'prop')
+ Member:
+ IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'prop') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'prop.field')
+ Member:
+ IFieldReferenceOperation: C C.field (OperationKind.FieldReference, Type: C) (Syntax: 'prop.field')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop.field')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: C, NarrowedType: C)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ (ImplicitReference)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
Next (Regular) Block[B2]
Block[B2] - Exit
Predecessors: [B1*2]
@@ -1116,9 +1130,9 @@ void M()
";
var expectedDiagnostics = new DiagnosticDescription[] {
- // file.cs(4,7): warning CS0169: The field 'C.field' is never used
- // C field;
- Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("C.field").WithLocation(4, 7)
+ // file.cs(4,7): warning CS0649: Field 'C.field' is never assigned to, and will always have its default value null
+ // C field;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("C.field", "null").WithLocation(4, 7)
};
VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
@@ -1137,27 +1151,33 @@ void M()
}
}
";
- string expectedOperationTree = @"
+ string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is { p ... d: null } y')
- Value:
+ Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
- Pattern:
+ Pattern:
IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: '{ prop.field: null } y') (InputType: C, NarrowedType: C, DeclaredSymbol: C y, MatchedType: C, DeconstructSymbol: null)
DeconstructionSubpatterns (0)
PropertySubpatterns (1):
- IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid) (Syntax: 'prop.field: null')
- Member:
- IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop.field')
- Children(1):
- IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop')
- Children(0)
- Pattern:
- IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
- Value:
- IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
- Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
- Operand:
- ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'prop')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop')
+ Children(0)
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'prop') (InputType: ?, NarrowedType: ?, DeclaredSymbol: null, MatchedType: ?, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'prop.field')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop.field')
+ Children(0)
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
";
var expectedDiagnostics = new DiagnosticDescription[] {
// file.cs(6,33): error CS0117: 'C' does not contain a definition for 'prop'
@@ -1170,7 +1190,7 @@ void M()
[CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)]
[Fact]
- public void IsPattern_BadRecursivePatternWithNestedPropertyPatterns_EventMember()
+ public void IsPattern_RecursivePatternWithNestedPropertyPatterns_EventMember()
{
string source = @"
class C
@@ -1183,30 +1203,36 @@ void M()
}
}
";
- string expectedOperationTree = @"
+ string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is { p ... n: null } y')
- Value:
+ Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
- Pattern:
+ Pattern:
IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: '{ prop.action: null } y') (InputType: C, NarrowedType: C, DeclaredSymbol: C y, MatchedType: C, DeconstructSymbol: null)
DeconstructionSubpatterns (0)
PropertySubpatterns (1):
- IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid) (Syntax: 'prop.action: null')
- Member:
- IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop.action')
- Children(1):
- IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
- Instance Receiver:
- IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
- Pattern:
- IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
- Value:
- IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
- Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
- Operand:
- ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
-";
- var expectedDiagnostics = new DiagnosticDescription[] {
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'prop')
+ Member:
+ IPropertyReferenceOperation: C C.prop { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'prop')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'prop')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'prop') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'prop.action')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'prop.action')
+ Children(0)
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+";
+ var expectedDiagnostics = new DiagnosticDescription[] {
// file.cs(5,32): warning CS0067: The event 'C.action' is never used
// public event System.Action action;
Diagnostic(ErrorCode.WRN_UnreferencedEvent, "action").WithArguments("C.action").WithLocation(5, 32),
@@ -1311,23 +1337,23 @@ void M1(object o, bool b)
var expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'o is C1 { P ... 1]: var x }')
- Value:
+ Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o')
- Pattern:
+ Pattern:
IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: 'C1 { Prop[1]: var x }') (InputType: System.Object, NarrowedType: C1, DeclaredSymbol: null, MatchedType: C1, DeconstructSymbol: null)
DeconstructionSubpatterns (0)
PropertySubpatterns (2):
IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid) (Syntax: 'Prop[1]')
- Member:
+ Member:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'Prop[1]')
Children(0)
- Pattern:
+ Pattern:
ITypePatternOperation (OperationKind.TypePattern, Type: null, IsInvalid) (Syntax: 'Prop[1]') (InputType: ?, NarrowedType: Prop[], MatchedType: Prop[])
IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid) (Syntax: 'var x')
- Member:
+ Member:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'var x')
Children(0)
- Pattern:
+ Pattern:
IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'var x') (InputType: ?, NarrowedType: ?, DeclaredSymbol: ?? x, MatchesNull: True)
";
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
index ac9c9a9a40547..fa5bf902c6c58 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
@@ -75976,6 +75976,39 @@ static void F(E e)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "e").WithLocation(11, 29));
}
+ [Theory]
+ [InlineData("null")]
+ [InlineData("not null")]
+ [InlineData("{}")]
+ public void OtherComparisonsAsPureNullTests_ExtendedProperties_PureNullTest(string pureTest)
+ {
+ var source =
+$@"#nullable enable
+class E
+{{
+ public E Property1 {{ get; set; }} = null!;
+ public object Property2 {{ get; set; }} = null!;
+}}
+class Program
+{{
+ static void F(E e)
+ {{
+ switch (e)
+ {{
+ case var x when e.Property1.Property2.ToString() == null: // 1
+ break;
+ case {{ Property1.Property2: {pureTest} }}:
+ break;
+ }}
+ }}
+}}";
+ var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyDiagnostics(
+ // (13,29): warning CS8602: Dereference of a possibly null reference.
+ // case var x when e.Property1.Property2.ToString() == null: // 1
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "e.Property1.Property2").WithLocation(13, 29));
+ }
+
[Fact, WorkItem(33526, "https://github.com/dotnet/roslyn/issues/33526")]
public void OtherComparisonsAreNotPureTest()
{
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs
index e4b0396cb364a..71f31d52c9ae9 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs
@@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
+using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
@@ -24,14 +25,14 @@ 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 }}}));
@@ -161,7 +162,7 @@ class C
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 };
}
@@ -200,7 +201,7 @@ C Prop2
}
public static void Main()
- {
+ {
Test(null);
Test(new());
Test(new() { Prop1 = new() });
@@ -276,7 +277,7 @@ public void ExtendedPropertyPatterns_04()
class C
{
public static void Main()
- {
+ {
_ = new C() is { Prop1.Prop2: {} };
_ = new C() is { Prop1->Prop2: {} };
_ = new C() is { Prop1!.Prop2: {} };
@@ -288,16 +289,16 @@ public static void Main()
";
var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics(
- // (6,26): error CS9000: Identifier or a simple member access expected.
+ // (6,26): error CS8918: 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.
+ // (7,26): error CS8918: 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.
+ // (8,26): error CS8918: 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.
+ // (9,26): error CS8918: 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] }'
@@ -315,50 +316,671 @@ public static void Main()
// (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.
+ // (11,26): error CS8918: Identifier or a simple member access expected.
// _ = new C() is { 1: {} };
Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "1").WithLocation(11, 26));
}
- [Fact]
+ [Fact, WorkItem(52956, "https://github.com/dotnet/roslyn/issues/52956")]
public void ExtendedPropertyPatterns_05()
{
var program = @"
class C
{
- C Prop1, Prop2;
+ C Field1, Field2, Field3, Field4;
+ public void M()
+ {
+ _ = this is { Field1.Field2.Field3: {} };
+ _ = this is { Field4: {} };
+ }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (4,7): warning CS0649: Field 'C.Field1' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field1").WithArguments("C.Field1", "null").WithLocation(4, 7),
+ // (4,15): warning CS0649: Field 'C.Field2' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field2").WithArguments("C.Field2", "null").WithLocation(4, 15),
+ // (4,23): warning CS0649: Field 'C.Field3' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field3").WithArguments("C.Field3", "null").WithLocation(4, 23),
+ // (4,31): warning CS0649: Field 'C.Field4' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field4").WithArguments("C.Field4", "null").WithLocation(4, 31)
+ );
+ }
+
+ [Fact, WorkItem(52956, "https://github.com/dotnet/roslyn/issues/52956")]
+ public void ExtendedPropertyPatterns_05_NestedRecursivePattern()
+ {
+ var program = @"
+class C
+{
+ C Field1, Field2, Field3, Field4;
+ public void M()
+ {
+ _ = this is { Field1: { Field2.Field3.Field4: not null } };
+ }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (4,7): warning CS0649: Field 'C.Field1' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field1").WithArguments("C.Field1", "null").WithLocation(4, 7),
+ // (4,15): warning CS0649: Field 'C.Field2' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field2").WithArguments("C.Field2", "null").WithLocation(4, 15),
+ // (4,23): warning CS0649: Field 'C.Field3' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field3").WithArguments("C.Field3", "null").WithLocation(4, 23),
+ // (4,31): warning CS0649: Field 'C.Field4' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3, Field4;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field4").WithArguments("C.Field4", "null").WithLocation(4, 31)
+ );
+ }
+
+ [Fact, WorkItem(52956, "https://github.com/dotnet/roslyn/issues/52956")]
+ public void ExtendedPropertyPatterns_05_Properties()
+ {
+ var program = @"
+class C
+{
+ C Prop1 { get; set; }
+ C Prop2 { get; set; }
+ C Prop3 { get; set; }
+ C Prop4 { get; set; }
+ public void M()
+ {
+ _ = this is { Prop1.Prop2.Prop3: {} };
+ _ = this is { Prop4: {} };
+ }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics();
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_IOperation_Properties()
+ {
+ var src = @"
+class Program
+{
+ static void M(A a)
+ /**/{
+ _ = a is { Prop1.Prop2.Prop3: null };
+ }/**/
+}
+class A { public B Prop1 => null; }
+class B { public C Prop2 => null; }
+class C { public object Prop3 => null; }
+";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
+ var isPattern = tree.GetRoot().DescendantNodes().OfType().Single();
+
+ VerifyOperationTree(comp, model.GetOperation(isPattern), @"
+IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is { Prop ... op3: null }')
+ Value:
+ IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: A) (Syntax: 'a')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ Prop1.Pro ... op3: null }') (InputType: A, NarrowedType: A, DeclaredSymbol: null, MatchedType: A, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1')
+ Member:
+ IPropertyReferenceOperation: B A.Prop1 { get; } (OperationKind.PropertyReference, Type: B) (Syntax: 'Prop1')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Prop1')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Prop1') (InputType: B, NarrowedType: B, DeclaredSymbol: null, MatchedType: B, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2')
+ Member:
+ IPropertyReferenceOperation: C B.Prop2 { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'Prop1.Prop2')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: B, IsImplicit) (Syntax: 'Prop1.Prop2')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2.Prop3')
+ Member:
+ IPropertyReferenceOperation: System.Object C.Prop3 { get; } (OperationKind.PropertyReference, Type: System.Object) (Syntax: 'Prop1.Prop2.Prop3')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Prop1.Prop2.Prop3')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: System.Object, NarrowedType: System.Object)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+");
+
+ var expectedFlowGraph = @"
+Block[B0] - Entry
+ Statements (0)
+ Next (Regular) Block[B1]
+Block[B1] - Block
+ Predecessors: [B0]
+ Statements (1)
+ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = a is { ... p3: null };')
+ Expression:
+ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: '_ = a is { ... op3: null }')
+ Left:
+ IDiscardOperation (Symbol: System.Boolean _) (OperationKind.Discard, Type: System.Boolean) (Syntax: '_')
+ Right:
+ IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is { Prop ... op3: null }')
+ Value:
+ IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: A) (Syntax: 'a')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ Prop1.Pro ... op3: null }') (InputType: A, NarrowedType: A, DeclaredSymbol: null, MatchedType: A, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1')
+ Member:
+ IPropertyReferenceOperation: B A.Prop1 { get; } (OperationKind.PropertyReference, Type: B) (Syntax: 'Prop1')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Prop1')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Prop1') (InputType: B, NarrowedType: B, DeclaredSymbol: null, MatchedType: B, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2')
+ Member:
+ IPropertyReferenceOperation: C B.Prop2 { get; } (OperationKind.PropertyReference, Type: C) (Syntax: 'Prop1.Prop2')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: B, IsImplicit) (Syntax: 'Prop1.Prop2')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Prop1.Prop2.Prop3')
+ Member:
+ IPropertyReferenceOperation: System.Object C.Prop3 { get; } (OperationKind.PropertyReference, Type: System.Object) (Syntax: 'Prop1.Prop2.Prop3')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Prop1.Prop2.Prop3')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: System.Object, NarrowedType: System.Object)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ (ImplicitReference)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ Next (Regular) Block[B2]
+Block[B2] - Exit
+ Predecessors: [B1]
+ Statements (0)
+";
+
+ VerifyFlowGraphAndDiagnosticsForTest(src, expectedFlowGraph, DiagnosticDescription.None, parseOptions: TestOptions.RegularPreview);
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_IOperation_FieldsInStructs()
+ {
+ var src = @"
+class Program
+{
+ static void M(A a)
+ /**/{
+ _ = a is { Field1.Field2.Field3: null, Field4: null };
+ }/**/
+}
+struct A { public B? Field1; public B? Field4; }
+struct B { public C? Field2; }
+struct C { public object Field3; }
+";
+ var expectedDiagnostics = new[]
+ {
+ // (9,22): warning CS0649: Field 'A.Field1' is never assigned to, and will always have its default value
+ // struct A { public B? Field1; public B? Field4; }
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field1").WithArguments("A.Field1", "").WithLocation(9, 22),
+ // (9,40): warning CS0649: Field 'A.Field4' is never assigned to, and will always have its default value
+ // struct A { public B? Field1; public B? Field4; }
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field4").WithArguments("A.Field4", "").WithLocation(9, 40),
+ // (10,22): warning CS0649: Field 'B.Field2' is never assigned to, and will always have its default value
+ // struct B { public C? Field2; }
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field2").WithArguments("B.Field2", "").WithLocation(10, 22),
+ // (11,26): warning CS0649: Field 'C.Field3' is never assigned to, and will always have its default value null
+ // struct C { public object Field3; }
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field3").WithArguments("C.Field3", "null").WithLocation(11, 26)
+ };
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyDiagnostics(expectedDiagnostics);
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
+ var isPattern = tree.GetRoot().DescendantNodes().OfType().Single();
+
+ VerifyOperationTree(comp, model.GetOperation(isPattern), @"
+IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is { Fiel ... ld4: null }')
+ Value:
+ IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: A) (Syntax: 'a')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ Field1.Fi ... ld4: null }') (InputType: A, NarrowedType: A, DeclaredSymbol: null, MatchedType: A, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (2):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1')
+ Member:
+ IFieldReferenceOperation: B? A.Field1 (OperationKind.FieldReference, Type: B?) (Syntax: 'Field1')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Field1')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Field1') (InputType: B, NarrowedType: B, DeclaredSymbol: null, MatchedType: B, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2')
+ Member:
+ IFieldReferenceOperation: C? B.Field2 (OperationKind.FieldReference, Type: C?) (Syntax: 'Field1.Field2')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: B, IsImplicit) (Syntax: 'Field1.Field2')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2.Field3')
+ Member:
+ IFieldReferenceOperation: System.Object C.Field3 (OperationKind.FieldReference, Type: System.Object) (Syntax: 'Field1.Field2.Field3')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Field1.Field2.Field3')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: System.Object, NarrowedType: System.Object)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null) (Syntax: 'Field4: null')
+ Member:
+ IFieldReferenceOperation: B? A.Field4 (OperationKind.FieldReference, Type: B?) (Syntax: 'Field4')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Field4')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: B?, NarrowedType: B?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: B?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+");
+
+ var expectedFlowGraph = @"
+Block[B0] - Entry
+ Statements (0)
+ Next (Regular) Block[B1]
+Block[B1] - Block
+ Predecessors: [B0]
+ Statements (1)
+ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = a is { ... d4: null };')
+ Expression:
+ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: '_ = a is { ... ld4: null }')
+ Left:
+ IDiscardOperation (Symbol: System.Boolean _) (OperationKind.Discard, Type: System.Boolean) (Syntax: '_')
+ Right:
+ IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is { Fiel ... ld4: null }')
+ Value:
+ IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: A) (Syntax: 'a')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null) (Syntax: '{ Field1.Fi ... ld4: null }') (InputType: A, NarrowedType: A, DeclaredSymbol: null, MatchedType: A, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (2):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1')
+ Member:
+ IFieldReferenceOperation: B? A.Field1 (OperationKind.FieldReference, Type: B?) (Syntax: 'Field1')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Field1')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Field1') (InputType: B, NarrowedType: B, DeclaredSymbol: null, MatchedType: B, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2')
+ Member:
+ IFieldReferenceOperation: C? B.Field2 (OperationKind.FieldReference, Type: C?) (Syntax: 'Field1.Field2')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: B, IsImplicit) (Syntax: 'Field1.Field2')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Field1.Field2.Field3')
+ Member:
+ IFieldReferenceOperation: System.Object C.Field3 (OperationKind.FieldReference, Type: System.Object) (Syntax: 'Field1.Field2.Field3')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Field1.Field2.Field3')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: System.Object, NarrowedType: System.Object)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ (ImplicitReference)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null) (Syntax: 'Field4: null')
+ Member:
+ IFieldReferenceOperation: B? A.Field4 (OperationKind.FieldReference, Type: B?) (Syntax: 'Field4')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'Field4')
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: B?, NarrowedType: B?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: B?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
+ (NullLiteral)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+ Next (Regular) Block[B2]
+Block[B2] - Exit
+ Predecessors: [B1]
+ Statements (0)
+";
+
+ VerifyFlowGraphAndDiagnosticsForTest(src, expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularPreview);
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_Explainer()
+ {
+ var src = @"
+class Program
+{
+ void M(A a)
+ {
+ _ = a switch // 1
+ {
+ { BProp.BoolProp: true } => 1
+ };
+
+ _ = a switch // 2
+ {
+ { BProp.IntProp: <= 0 } => 1
+ };
+ }
+}
+class A { public B BProp => null; }
+class B
+{
+ public bool BoolProp => true;
+ public int IntProp => 0;
+}
+";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyDiagnostics(
+ // (6,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ BProp: { BoolProp: false } }' is not covered.
+ // _ = a switch // 1
+ Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ BProp: { BoolProp: false } }").WithLocation(6, 15),
+ // (11,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ BProp: { IntProp: 1 } }' is not covered.
+ // _ = a switch // 2
+ Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ BProp: { IntProp: 1 } }").WithLocation(11, 15)
+ );
+ }
+
+ [Fact, WorkItem(52956, "https://github.com/dotnet/roslyn/issues/52956")]
+ public void ExtendedPropertyPatterns_BadMemberAccess()
+ {
+ var program = @"
+class C
+{
+ C Field1, Field2, Field3;
public static void Main()
- {
- _ = new C() is { Prop1.Prop2: {} };
- _ = new C() is { Prop1?.Prop2: {} };
- _ = new C() is { Missing: null, Prop1.Prop2: {} };
+ {
+ _ = new C() is { Field1?.Field2: {} }; // 1
+ _ = new C() is { Field1!.Field2: {} }; // 2
+ _ = new C() is { Missing: null }; // 3
+ _ = new C() is { Field3.Missing: {} }; // 4
+ _ = new C() is { Missing1.Missing2: {} }; // 5
}
}
";
- 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));
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (4,7): warning CS0169: The field 'C.Field1' is never used
+ // C Field1, Field2, Field3;
+ Diagnostic(ErrorCode.WRN_UnreferencedField, "Field1").WithArguments("C.Field1").WithLocation(4, 7),
+ // (4,15): warning CS0169: The field 'C.Field2' is never used
+ // C Field1, Field2, Field3;
+ Diagnostic(ErrorCode.WRN_UnreferencedField, "Field2").WithArguments("C.Field2").WithLocation(4, 15),
+ // (4,23): warning CS0649: Field 'C.Field3' is never assigned to, and will always have its default value null
+ // C Field1, Field2, Field3;
+ Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field3").WithArguments("C.Field3", "null").WithLocation(4, 23),
+ // (7,26): error CS8918: Identifier or a simple member access expected.
+ // _ = new C() is { Field1?.Field2: {} }; // 1
+ Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Field1?.Field2").WithLocation(7, 26),
+ // (8,26): error CS8918: Identifier or a simple member access expected.
+ // _ = new C() is { Field1!.Field2: {} }; // 2
+ Diagnostic(ErrorCode.ERR_InvalidNameInSubpattern, "Field1!").WithLocation(8, 26),
+ // (9,26): error CS0117: 'C' does not contain a definition for 'Missing'
+ // _ = new C() is { Missing: null }; // 3
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing").WithArguments("C", "Missing").WithLocation(9, 26),
+ // (10,33): error CS0117: 'C' does not contain a definition for 'Missing'
+ // _ = new C() is { Field3.Missing: {} }; // 4
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing").WithArguments("C", "Missing").WithLocation(10, 33),
+ // (11,26): error CS0117: 'C' does not contain a definition for 'Missing1'
+ // _ = new C() is { Missing1.Missing2: {} }; // 5
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing1").WithArguments("C", "Missing1").WithLocation(11, 26)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_IOperationOnMissing()
+ {
+ var program = @"
+class C
+{
+ public void M()
+ {
+ _ = this is { Missing: null };
+ }
+}
+";
+ var comp = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyEmitDiagnostics(
+ // (6,23): error CS0117: 'C' does not contain a definition for 'Missing'
+ // _ = this is { Missing: null };
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing").WithArguments("C", "Missing").WithLocation(6, 23)
+ );
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
+ var isPattern = tree.GetRoot().DescendantNodes().OfType().Single();
+
+ VerifyOperationTree(comp, model.GetOperation(isPattern), @"
+IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is { M ... ing: null }')
+ Value:
+ IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: '{ Missing: null }') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid) (Syntax: 'Missing: null')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'Missing')
+ Children(0)
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+");
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_IOperationOnNestedMissing()
+ {
+ var program = @"
+class C
+{
+ int Property { get; set; }
+ public void M()
+ {
+ _ = this is { Property.Missing: null };
+ }
+}
+";
+ var comp = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyEmitDiagnostics(
+ // (7,32): error CS0117: 'int' does not contain a definition for 'Missing'
+ // _ = this is { Property.Missing: null };
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing").WithArguments("int", "Missing").WithLocation(7, 32)
+ );
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
+ var isPattern = tree.GetRoot().DescendantNodes().OfType().Single();
+
+ VerifyOperationTree(comp, model.GetOperation(isPattern), @"
+IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is { P ... ing: null }')
+ Value:
+ IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: '{ Property. ... ing: null }') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsImplicit) (Syntax: 'Property')
+ Member:
+ IPropertyReferenceOperation: System.Int32 C.Property { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'Property')
+ Instance Receiver:
+ IInstanceReferenceOperation (ReferenceKind: PatternInput) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Property')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsImplicit) (Syntax: 'Property') (InputType: System.Int32, NarrowedType: System.Int32, DeclaredSymbol: null, MatchedType: System.Int32, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'Property.Missing')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'Property.Missing')
+ Children(0)
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+");
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_IOperationOnTwoMissing()
+ {
+ var program = @"
+class C
+{
+ public void M()
+ {
+ _ = this is { Missing1.Missing2: null };
+ }
+}
+";
+ var comp = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ comp.VerifyEmitDiagnostics(
+ // (6,23): error CS0117: 'C' does not contain a definition for 'Missing1'
+ // _ = this is { Missing1.Missing2: null };
+ Diagnostic(ErrorCode.ERR_NoSuchMember, "Missing1").WithArguments("C", "Missing1").WithLocation(6, 23)
+ );
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
+ var isPattern = tree.GetRoot().DescendantNodes().OfType().Single();
+
+ VerifyOperationTree(comp, model.GetOperation(isPattern), @"
+IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is { M ... ng2: null }')
+ Value:
+ IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this')
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid) (Syntax: '{ Missing1. ... ng2: null }') (InputType: C, NarrowedType: C, DeclaredSymbol: null, MatchedType: C, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'Missing1')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'Missing1')
+ Children(0)
+ Pattern:
+ IRecursivePatternOperation (OperationKind.RecursivePattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'Missing1') (InputType: ?, NarrowedType: ?, DeclaredSymbol: null, MatchedType: ?, DeconstructSymbol: null)
+ DeconstructionSubpatterns (0)
+ PropertySubpatterns (1):
+ IPropertySubpatternOperation (OperationKind.PropertySubpattern, Type: null, IsInvalid, IsImplicit) (Syntax: 'Missing1.Missing2')
+ Member:
+ IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: 'Missing1.Missing2')
+ Children(0)
+ Pattern:
+ IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: 'null') (InputType: ?, NarrowedType: ?)
+ Value:
+ IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsImplicit) (Syntax: 'null')
+ Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
+ Operand:
+ ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null')
+");
+ }
+
+ [Fact, WorkItem(53484, "https://github.com/dotnet/roslyn/issues/53484")]
+ public void ExtendedPropertyPatterns_SuppressionOnPattern()
+ {
+ var program = @"
+#nullable enable
+public class ContainerType
+{
+ public class Type
+ {
+ public void M()
+ {
+ const Type c = null!;
+ if (this is c!) {}
+ if (this is (c!)) {}
+ if (this is Type!) {} // 1
+ if (this is ContainerType!.Type) {} // 2
+ if (this is ContainerType.Type!) {} // 3
+ }
+ }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (12,25): error CS8598: The suppression operator is not allowed in this context
+ // if (this is Type!) {} // 1
+ Diagnostic(ErrorCode.ERR_IllegalSuppression, "Type!").WithLocation(12, 25),
+ // (13,25): error CS8598: The suppression operator is not allowed in this context
+ // if (this is ContainerType!.Type) {} // 2
+ Diagnostic(ErrorCode.ERR_IllegalSuppression, "ContainerType").WithLocation(13, 25),
+ // (14,25): error CS8598: The suppression operator is not allowed in this context
+ // if (this is ContainerType.Type!) {} // 3
+ Diagnostic(ErrorCode.ERR_IllegalSuppression, "ContainerType.Type!").WithLocation(14, 25)
+ );
+ }
+
+ [Fact, WorkItem(53484, "https://github.com/dotnet/roslyn/issues/53484")]
+ public void ExtendedPropertyPatterns_PointerAccessInPattern()
+ {
+ var program = @"
+public class Type
+{
+ public unsafe void M(S* s)
+ {
+ if (0 is s->X) {}
+ }
+}
+
+public struct S
+{
+ public int X;
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns, options: TestOptions.UnsafeDebugDll);
+ compilation.VerifyEmitDiagnostics(
+ // (6,18): error CS0150: A constant value is expected
+ // if (0 is s->X) {}
+ Diagnostic(ErrorCode.ERR_ConstantExpected, "s->X").WithLocation(6, 18)
+ );
}
[Fact]
@@ -382,7 +1004,7 @@ class P
}
";
var compilation = CreatePatternCompilation(source);
- compilation.VerifyDiagnostics(
+ compilation.VerifyEmitDiagnostics(
// (14,14): warning CS0649: Field 'P.Y' is never assigned to, and will always have its default value null
// public P Y;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Y").WithArguments("P.Y", "null").WithLocation(14, 14)
@@ -459,7 +1081,7 @@ interface P : I1, I2
}
";
var compilation = CreatePatternCompilation(source);
- compilation.VerifyDiagnostics(
+ compilation.VerifyEmitDiagnostics(
// (8,34): error CS0229: Ambiguity between 'I1.X' and 'I2.X'
// Console.WriteLine(p is { X.Y: {}, Y.X: {}, });
Diagnostic(ErrorCode.ERR_AmbigMember, "X").WithArguments("I1.X", "I2.X").WithLocation(8, 34),
@@ -513,7 +1135,7 @@ class P
}
";
var compilation = CreatePatternCompilation(source);
- compilation.VerifyDiagnostics(
+ compilation.VerifyEmitDiagnostics(
// (8,34): error CS0117: 'P' does not contain a definition for 'X'
// Console.WriteLine(p is { X: 3, Y: 4 });
Diagnostic(ErrorCode.ERR_NoSuchMember, "X").WithArguments("P", "X").WithLocation(8, 34)
@@ -565,7 +1187,7 @@ struct S
}
";
var compilation = CreatePatternCompilation(source);
- compilation.VerifyDiagnostics(
+ compilation.VerifyEmitDiagnostics(
// (17,14): warning CS0649: Field 'S.Y' is never assigned to, and will always have its default value null
// public C Y;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Y").WithArguments("S.Y", "null").WithLocation(17, 14)
@@ -628,34 +1250,82 @@ struct S
var xNameType = model.GetTypeInfo(xName);
Assert.Equal("S?", xNameType.Type.ToTestDisplayString());
Assert.Equal("S?", xNameType.ConvertedType.ToTestDisplayString());
+
+ var verifier = CompileAndVerify(compilation);
+ verifier.VerifyIL("Program.Main", @"
+{
+ // Code size 92 (0x5c)
+ .maxstack 2
+ .locals init (C V_0,
+ S? V_1,
+ S V_2)
+ IL_0000: nop
+ IL_0001: newobj ""C..ctor()""
+ IL_0006: stloc.0
+ IL_0007: ldloc.0
+ IL_0008: brfalse.s IL_002b
+ IL_000a: ldloc.0
+ IL_000b: callvirt ""S? C.X.get""
+ IL_0010: stloc.1
+ IL_0011: ldloca.s V_1
+ IL_0013: call ""bool S?.HasValue.get""
+ IL_0018: brfalse.s IL_002b
+ IL_001a: ldloca.s V_1
+ IL_001c: call ""S S?.GetValueOrDefault()""
+ IL_0021: ldfld ""C S.Y""
+ IL_0026: ldnull
+ IL_0027: cgt.un
+ IL_0029: br.s IL_002c
+ IL_002b: ldc.i4.0
+ IL_002c: call ""void System.Console.WriteLine(bool)""
+ IL_0031: nop
+ IL_0032: ldloca.s V_2
+ IL_0034: initobj ""S""
+ IL_003a: ldloc.2
+ IL_003b: ldfld ""C S.Y""
+ IL_0040: stloc.0
+ IL_0041: ldloc.0
+ IL_0042: brfalse.s IL_0054
+ IL_0044: ldloc.0
+ IL_0045: callvirt ""S? C.X.get""
+ IL_004a: stloc.1
+ IL_004b: ldloca.s V_1
+ IL_004d: call ""bool S?.HasValue.get""
+ IL_0052: br.s IL_0055
+ IL_0054: ldc.i4.0
+ IL_0055: call ""void System.Console.WriteLine(bool)""
+ IL_005a: nop
+ IL_005b: ret
+}
+");
}
[Fact]
- public void ExtendedPropertyPatterns_Nullability_01()
+ public void ExtendedPropertyPatterns_Nullability_Properties()
{
var program = @"
#nullable enable
class C {
C? Prop { get; }
public void M() {
- if (this is { Prop.Prop: null })
+ if (this is { Prop.Prop: null })
{
this.Prop.ToString();
this.Prop.Prop.ToString(); // 1
}
- if (this is { Prop.Prop: {} })
+ if (this is { Prop.Prop: {} })
{
this.Prop.ToString();
this.Prop.Prop.ToString();
}
- if (this is { Prop: null } &&
- this is { Prop.Prop: null })
+ if (this is { Prop: null } &&
+ this is { Prop.Prop: null })
{
this.Prop.ToString();
this.Prop.Prop.ToString(); // 2
}
- if (this is { Prop: null } ||
- this is { Prop.Prop: null })
+ if (this is { Prop: null } ||
+ this is { Prop.Prop: null })
{
this.Prop.ToString(); // 3
this.Prop.Prop.ToString(); // 4
@@ -664,7 +1334,7 @@ public void M() {
}
";
var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
- compilation.VerifyDiagnostics(
+ compilation.VerifyEmitDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// this.Prop.Prop.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "this.Prop.Prop").WithLocation(9, 13),
@@ -680,59 +1350,401 @@ public void M() {
}
[Fact]
- public void ExtendedPropertyPatterns_Nullability_02()
+ public void ExtendedPropertyPatterns_Nullability_AnnotatedFields()
{
var program = @"
#nullable enable
class C {
public void M(C1 c1) {
- if (c1 is { Prop.Prop: null })
+ if (c1 is { Prop1.Prop2: null })
{
- c1.Prop.ToString();
- c1.Prop.Prop.ToString(); // 1
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString(); // 1
}
- if (c1 is { Prop.Prop: {} })
+ if (c1 is { Prop1.Prop2: {} })
{
- c1.Prop.ToString();
- c1.Prop.Prop.ToString();
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString();
}
- if (c1 is { Prop: null } &&
- c1 is { Prop.Prop: null })
+ if (c1 is { Prop1: null } &&
+ c1 is { Prop1.Prop2: null })
{
- c1.Prop.ToString();
- c1.Prop.Prop.ToString(); // 2
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString(); // 2
}
- if (c1 is { Prop: null } ||
- c1 is { Prop.Prop: null })
+ if (c1 is { Prop1: null } ||
+ c1 is { Prop1.Prop2: null })
{
- c1.Prop.ToString(); // 3
- c1.Prop.Prop.ToString(); // 4
+ c1.Prop1.ToString(); // 3
+ c1.Prop1.Prop2.ToString(); // 4
}
}
}
-class C1 { public C2? Prop; }
-class C2 { public object? Prop; }
+class C1 { public C2? Prop1 = null; }
+class C2 { public object? Prop2 = null; }
";
var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
- compilation.VerifyDiagnostics(
- // (8,13): warning CS8602: Dereference of a possibly null reference.
- // c1.Prop.Prop.ToString(); // 1
- Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop.Prop").WithLocation(8, 13),
- // (19,13): warning CS8602: Dereference of a possibly null reference.
- // c1.Prop.Prop.ToString(); // 2
- Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop.Prop").WithLocation(19, 13),
- // (24,13): warning CS8602: Dereference of a possibly null reference.
- // c1.Prop.ToString(); // 3
- Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop").WithLocation(24, 13),
- // (25,13): warning CS8602: Dereference of a possibly null reference.
- // c1.Prop.Prop.ToString(); // 4
- Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop.Prop").WithLocation(25, 13),
- // (29,23): warning CS0649: Field 'C1.Prop' is never assigned to, and will always have its default value null
- // class C1 { public C2? Prop; }
- Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Prop").WithArguments("C1.Prop", "null").WithLocation(29, 23),
- // (30,27): warning CS0649: Field 'C2.Prop' is never assigned to, and will always have its default value null
- // class C2 { public object? Prop; }
- Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Prop").WithArguments("C2.Prop", "null").WithLocation(30, 27));
+ compilation.VerifyEmitDiagnostics(
+ // (8,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 1
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(8, 13),
+ // (19,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 2
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(19, 13),
+ // (24,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.ToString(); // 3
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1").WithLocation(24, 13),
+ // (25,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 4
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(25, 13)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_Nullability_UnannotatedFields()
+ {
+ var program = @"
+#nullable enable
+class C
+{
+ public void M1(C1 c1)
+ {
+ if (c1 is { Prop1.Prop2: null })
+ {
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString(); // 1
+ }
+ else
+ {
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString();
+ }
+ }
+
+ public void M2(C1 c1)
+ {
+ if (c1 is { Prop1.Prop2: {} })
+ {
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString();
+ }
+ }
+
+ public void M3(C1 c1)
+ {
+ if (c1 is { Prop1: null } &&
+ c1 is { Prop1.Prop2: null })
+ {
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString(); // 2
+ }
+ else
+ {
+ c1.Prop1.ToString(); // 3
+ c1.Prop1.Prop2.ToString();
+ }
+ }
+
+ public void M4(C1 c1)
+ {
+ if (c1 is { Prop1: null } ||
+ c1 is { Prop1.Prop2: null })
+ {
+ c1.Prop1.ToString(); // 4
+ c1.Prop1.Prop2.ToString(); // 5
+ }
+ else
+ {
+ c1.Prop1.ToString();
+ c1.Prop1.Prop2.ToString();
+ }
+ }
+}
+
+class C1 { public C2 Prop1 = null!; }
+class C2 { public object Prop2 = null!; }
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (10,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 1
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(10, 13),
+ // (34,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 2
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(34, 13),
+ // (38,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.ToString(); // 3
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1").WithLocation(38, 13),
+ // (48,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.ToString(); // 4
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1").WithLocation(48, 13),
+ // (49,13): warning CS8602: Dereference of a possibly null reference.
+ // c1.Prop1.Prop2.ToString(); // 5
+ Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1.Prop1.Prop2").WithLocation(49, 13)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ExpressionColonInPositionalPattern()
+ {
+ var source = @"
+class C
+{
+ C Property { get; set; }
+
+ void M()
+ {
+ _ = this is (Property.Property: null, Property: null);
+ }
+
+ public void Deconstruct(out C c1, out C c2)
+ => throw null;
+}
+";
+ var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular9);
+ compilation.VerifyEmitDiagnostics(
+ // (8,22): error CS8652: The feature 'extended property patterns' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // _ = this is (Property.Property: null, Property: null);
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property.Property").WithArguments("extended property patterns").WithLocation(8, 22),
+ // (8,22): error CS1001: Identifier expected
+ // _ = this is (Property.Property: null, Property: null);
+ Diagnostic(ErrorCode.ERR_IdentifierExpected, "Property.Property").WithLocation(8, 22),
+ // (8,47): error CS8517: The name 'Property' does not match the corresponding 'Deconstruct' parameter 'c2'.
+ // _ = this is (Property.Property: null, Property: null);
+ Diagnostic(ErrorCode.ERR_DeconstructParameterNameMismatch, "Property").WithArguments("Property", "c2").WithLocation(8, 47)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ExpressionColonInITuplePattern()
+ {
+ var source = @"
+class C
+{
+ void M()
+ {
+ System.Runtime.CompilerServices.ITuple t = null;
+ var r = t is (X.Y: 3, Y.Z: 4);
+ }
+}
+namespace System.Runtime.CompilerServices
+{
+ public interface ITuple
+ {
+ int Length { get; }
+ object this[int index] { get; }
+ }
+}
+";
+ var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (7,23): error CS1001: Identifier expected
+ // var r = t is (X.Y: 3, Y.Z: 4);
+ Diagnostic(ErrorCode.ERR_IdentifierExpected, "X.Y").WithLocation(7, 23),
+ // (7,31): error CS1001: Identifier expected
+ // var r = t is (X.Y: 3, Y.Z: 4);
+ Diagnostic(ErrorCode.ERR_IdentifierExpected, "Y.Z").WithLocation(7, 31)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ExpressionColonInValueTuplePattern()
+ {
+ var source = @"
+class C
+{
+ void M()
+ {
+ _ = (1, 2) is (X.Y: 3, Y.Z: 4);
+ }
+}
+namespace System.Runtime.CompilerServices
+{
+ public interface ITuple
+ {
+ int Length { get; }
+ object this[int index] { get; }
+ }
+}
+";
+ var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (6,24): error CS1001: Identifier expected
+ // _ = (1, 2) is (X.Y: 3, Y.Z: 4);
+ Diagnostic(ErrorCode.ERR_IdentifierExpected, "X.Y").WithLocation(6, 24),
+ // (6,32): error CS1001: Identifier expected
+ // _ = (1, 2) is (X.Y: 3, Y.Z: 4);
+ Diagnostic(ErrorCode.ERR_IdentifierExpected, "Y.Z").WithLocation(6, 32)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ObsoleteProperty()
+ {
+ var program = @"
+using System;
+class C
+{
+ public void M1(C1 c1)
+ {
+ _ = c1 is { Prop1.Prop2: null };
+ }
+}
+
+class C1
+{
+ [ObsoleteAttribute(""error Prop1"", true)]
+ public C2 Prop1 { get; set; }
+}
+class C2
+{
+ [ObsoleteAttribute(""error Prop2"", true)]
+ public object Prop2 { get; set; }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (7,21): error CS0619: 'C1.Prop1' is obsolete: 'error Prop1'
+ // _ = c1 is { Prop1.Prop2: null };
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop1").WithArguments("C1.Prop1", "error Prop1").WithLocation(7, 21),
+ // (7,27): error CS0619: 'C2.Prop2' is obsolete: 'error Prop2'
+ // _ = c1 is { Prop1.Prop2: null };
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop2").WithArguments("C2.Prop2", "error Prop2").WithLocation(7, 27)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ObsoleteAccessor()
+ {
+ var program = @"
+using System;
+class C
+{
+ public void M1(C1 c1)
+ {
+ _ = c1 is { Prop1.Prop2: null };
+ }
+}
+
+class C1
+{
+ public C2 Prop1
+ {
+ [ObsoleteAttribute(""error Prop1"", true)]
+ get;
+ set;
+ }
+}
+class C2
+{
+ public object Prop2
+ {
+ get;
+ [ObsoleteAttribute(""error Prop2"", true)]
+ set;
+ }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (7,21): error CS0619: 'C1.Prop1.get' is obsolete: 'error Prop1'
+ // _ = c1 is { Prop1.Prop2: null };
+ Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop1.Prop2").WithArguments("C1.Prop1.get", "error Prop1").WithLocation(7, 21)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_InaccessibleProperty()
+ {
+ var program = @"
+using System;
+class C
+{
+ public void M1(C1 c1)
+ {
+ _ = c1 is { Prop1.Prop2: null };
+ }
+}
+
+class C1
+{
+ private C2 Prop1 { get; set; }
+}
+class C2
+{
+ private object Prop2 { get; set; }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (7,21): error CS0122: 'C1.Prop1' is inaccessible due to its protection level
+ // _ = c1 is { Prop1.Prop2: null };
+ Diagnostic(ErrorCode.ERR_BadAccess, "Prop1").WithArguments("C1.Prop1").WithLocation(7, 21)
+ );
+ }
+
+ [Fact]
+ public void ExtendedPropertyPatterns_ExpressionTree()
+ {
+ var program = @"
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+class C
+{
+ public void M1(C1 c1)
+ {
+ Expression> f = (c1) => c1 is { Prop1.Prop2: null };
+ }
+}
+
+class C1
+{
+ public C2 Prop1 { get; set; }
+}
+class C2
+{
+ public object Prop2 { get; set; }
+}
+";
+ var compilation = CreateCompilation(program, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
+ compilation.VerifyEmitDiagnostics(
+ // (9,48): error CS8122: An expression tree may not contain an 'is' pattern-matching operator.
+ // Expression> f = (c1) => c1 is { Prop1.Prop2: null };
+ Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, "c1 is { Prop1.Prop2: null }").WithLocation(9, 48)
+ );
+ }
+
+ public class FlowAnalysisTests : FlowTestBase
+ {
+ [Fact]
+ public void RegionInIsPattern01()
+ {
+ var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(@"
+class C
+{
+ static void M(object o)
+ {
+ _ = o switch
+ {
+ string { Length: 0 } s => /**/s.ToString()/**/,
+ _ = throw null
+ };
+ }
+}");
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
+ Assert.Equal("s", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
+ Assert.Equal("s", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
+ Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
+ Assert.Equal("o, s", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
+ Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
+ }
}
}
}
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs
index ee47e59f6cbde..c5ad3311d7edb 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs
@@ -865,5 +865,63 @@ public void ExtendedPropertySubpattern_15()
}
EOF();
}
+
+ [Fact]
+ public void ExtendedPropertySubpattern_InPositionalPattern()
+ {
+ UsingExpression(@"e is ( a.b.c: p )");
+
+ N(SyntaxKind.IsPatternExpression);
+ {
+ N(SyntaxKind.IdentifierName);
+ {
+ N(SyntaxKind.IdentifierToken, "e");
+ }
+ N(SyntaxKind.IsKeyword);
+ N(SyntaxKind.RecursivePattern);
+ {
+ N(SyntaxKind.PositionalPatternClause);
+ {
+ N(SyntaxKind.OpenParenToken);
+ 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.CloseParenToken);
+ }
+ }
+ }
+ EOF();
+ }
}
}
diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
index 02482050386d7..0e87076b9dafd 100644
--- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
@@ -971,10 +971,9 @@ public void TestNormalizeBlockAnonymousFunctions(string actual, string expected)
TestNormalizeStatement(actual, expected);
}
- [Fact(Skip = "PROTOTYPE")]
+ [Fact]
public void TestNormalizeExtendedPropertyPattern()
{
- // PROTOTYPE: we may need to flow https://github.com/dotnet/roslyn/pull/52680 into the feature branch first
var text = "_ = this is{Property . Property :2};";
var expected = @"_ = this is { Property.Property: 2 };";
diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs
index 3be5f5a769a20..d690671b2b71e 100644
--- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs
+++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs
@@ -57,9 +57,9 @@ public static void Verify(string expectedOperationTree, string actualOperationTr
{
char[] newLineChars = Environment.NewLine.ToCharArray();
string actual = actualOperationTree.Trim(newLineChars);
- actual = actual.Replace("\"", "\"\"");
+ actual = actual.Replace("\"", "\"\"").Replace(" \n", "\n").Replace(" \r", "\r");
expectedOperationTree = expectedOperationTree.Trim(newLineChars);
- expectedOperationTree = expectedOperationTree.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
+ expectedOperationTree = expectedOperationTree.Replace("\r\n", "\n").Replace(" \n", "\n").Replace("\n", Environment.NewLine);
expectedOperationTree = expectedOperationTree.Replace("\"", "\"\"");
AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedOperationTree, actual);
diff --git a/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs b/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs
index f5a0a5770ae48..25aa680d6c23d 100644
--- a/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs
+++ b/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs
@@ -8891,6 +8891,49 @@ class Blah
}");
}
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
+ public async Task TestExtendedPropertyPatternInIsPattern()
+ {
+ await TestInRegularAndScriptAsync(
+@"
+class C
+{
+ Blah SomeBlah { get; set; }
+
+ void M2()
+ {
+ object o = null;
+ if (o is C { SomeBlah.[|X|]: (y: 1, z: 2) })
+ {
+ }
+ }
+
+ class Blah
+ {
+ }
+}
+" + TestResources.NetFX.ValueTuple.tuplelib_cs,
+@"
+class C
+{
+ Blah SomeBlah { get; set; }
+
+ void M2()
+ {
+ object o = null;
+ if (o is C { SomeBlah.X: (y: 1, z: 2) })
+ {
+ }
+ }
+
+ class Blah
+ {
+ public (int y, int z) X { get; internal set; }
+ }
+}
+" + TestResources.NetFX.ValueTuple.tuplelib_cs, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
+ }
+
[WorkItem(9090, "https://github.com/dotnet/roslyn/issues/9090")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestPropertyPatternInIsPattern9()
diff --git a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs
index f087434156dab..f9b2bb79947a6 100644
--- a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs
+++ b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs
@@ -3210,6 +3210,60 @@ public void M()
{ Color: [||]
}
}
+";
+ await TestAsync(markup, "global::Color", TestMode.Position);
+ }
+
+ [Fact]
+ [Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
+ public async Task TestEnumInPatterns_SwitchStatement_ExtendedPropertyPattern()
+ {
+ var markup = @"
+public enum Color
+{
+ Red,
+ Green,
+}
+
+class C
+{
+ public C AnotherC { get; }
+ public Color Color { get; }
+
+ public void M()
+ {
+ switch (this)
+ {
+ case { AnotherC.Color: [||]
+ }
+}
+";
+ await TestAsync(markup, "global::Color", TestMode.Position);
+ }
+
+ [Fact]
+ [Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
+ public async Task TestEnumInPatterns_SwitchStatement_ExtendedPropertyPattern_Field()
+ {
+ var markup = @"
+public enum Color
+{
+ Red,
+ Green,
+}
+
+class C
+{
+ public C AnotherC { get; }
+ public Color Color;
+
+ public void M()
+ {
+ switch (this)
+ {
+ case { AnotherC.Color: [||]
+ }
+}
";
await TestAsync(markup, "global::Color", TestMode.Position);
}
diff --git a/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs b/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs
index 900aaafd20c99..e5e4740c80d80 100644
--- a/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs
+++ b/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs
@@ -160,7 +160,7 @@ private static bool IsLegal(
return true;
}
- if (expression.IsParentKind(SyntaxKind.NameColon) &&
+ if (expression.IsParentKind(SyntaxKind.NameColon, SyntaxKind.ExpressionColon) &&
expression.Parent.IsParentKind(SyntaxKind.Subpattern))
{
return true;
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs
index 7caa73b8e5150..b72e782fead03 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs
@@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
+using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
@@ -159,6 +160,7 @@ protected override IEnumerable InferTypesWorker_DoNotCallDire
ConstantPatternSyntax constantPattern => InferTypeInConstantPattern(constantPattern),
DoStatementSyntax doStatement => InferTypeInDoStatement(doStatement),
EqualsValueClauseSyntax equalsValue => InferTypeInEqualsValueClause(equalsValue),
+ ExpressionColonSyntax expressionColon => InferTypeInExpressionColon(expressionColon),
ExpressionStatementSyntax _ => InferTypeInExpressionStatement(),
ForEachStatementSyntax forEachStatement => InferTypeInForEachStatement(forEachStatement, expression),
ForStatementSyntax forStatement => InferTypeInForStatement(forStatement, expression),
@@ -224,6 +226,7 @@ protected override IEnumerable InferTypesWorker_DoNotCallDire
DefaultExpressionSyntax defaultExpression => InferTypeInDefaultExpression(defaultExpression),
DoStatementSyntax doStatement => InferTypeInDoStatement(doStatement, token),
EqualsValueClauseSyntax equalsValue => InferTypeInEqualsValueClause(equalsValue, token),
+ ExpressionColonSyntax expressionColon => InferTypeInExpressionColon(expressionColon, token),
ExpressionStatementSyntax _ => InferTypeInExpressionStatement(token),
ForEachStatementSyntax forEachStatement => InferTypeInForEachStatement(forEachStatement, previousToken: token),
ForStatementSyntax forStatement => InferTypeInForStatement(forStatement, previousToken: token),
@@ -1458,12 +1461,11 @@ 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)
+ subpattern.ExpressionColon != null)
{
- var result = ArrayBuilder.GetInstance();
+ using var result = TemporaryArray.Empty;
- foreach (var symbol in this.SemanticModel.GetSymbolInfo(subpattern.NameColon.Name).GetAllSymbols())
+ foreach (var symbol in this.SemanticModel.GetSymbolInfo(subpattern.ExpressionColon.Expression).GetAllSymbols())
{
switch (symbol)
{
@@ -1476,7 +1478,7 @@ private IEnumerable InferTypeInSubpattern(
}
}
- return result.ToImmutableAndFree();
+ return result.ToImmutableAndClear();
}
return SpecializedCollections.EmptyEnumerable();
@@ -1559,7 +1561,6 @@ 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();
@@ -1665,6 +1666,21 @@ private IEnumerable InferTypeInNameColon(NameColonSyntax name
};
}
+ private IEnumerable InferTypeInExpressionColon(ExpressionColonSyntax expressionColon, SyntaxToken previousToken)
+ {
+ if (previousToken != expressionColon.ColonToken)
+ {
+ // Must follow the colon token.
+ return SpecializedCollections.EmptyEnumerable();
+ }
+
+ return expressionColon.Parent switch
+ {
+ SubpatternSyntax subPattern => InferTypeInSubpattern(subPattern, subPattern.Pattern),
+ _ => SpecializedCollections.EmptyEnumerable()
+ };
+ }
+
private IEnumerable InferTypeInMemberAccessExpression(
MemberAccessExpressionSyntax memberAccessExpression,
ExpressionSyntax expressionOpt = null,
@@ -1843,6 +1859,16 @@ private IEnumerable InferTypeInNameColon(NameColonSyntax name
return SpecializedCollections.EmptyEnumerable();
}
+ private IEnumerable InferTypeInExpressionColon(ExpressionColonSyntax expressionColon)
+ {
+ if (expressionColon.Parent is SubpatternSyntax subpattern)
+ {
+ return GetPatternTypes(subpattern.Pattern);
+ }
+
+ return SpecializedCollections.EmptyEnumerable();
+ }
+
private IEnumerable InferTypeInNameEquals(NameEqualsSyntax nameEquals, SyntaxToken? previousToken = null)
{
if (previousToken == nameEquals.EqualsToken)