提交 eba2520d 编写于 作者: N Neal Gafter

Changes to pattern-matching implementation per code review.

上级 97d47292
......@@ -223,7 +223,6 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node,
var syntax = e as IsPatternExpressionSyntax;
var identifier = syntax?.Expression as IdentifierNameSyntax;
if (identifier == null) throw ExceptionUtilities.UnexpectedValue(syntax?.Expression.Kind() ?? e.Kind());
var propName = identifier.Identifier;
var boundMember = BindPropertyPatternMember(type, identifier, diagnostics);
var boundPattern = BindPattern(syntax.Pattern, null, boundMember.Type, boundMember.HasErrors, diagnostics);
result.Add(new BoundSubPropertyPattern(e, boundMember, boundPattern, boundPattern.HasErrors));
......@@ -232,7 +231,7 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node,
return result.ToImmutableAndFree();
}
// returns BadBoundExpression or BoundObjectInitializerMember
// returns BadBoundExpression or BoundPropertyPatternMember
private BoundExpression BindPropertyPatternMember(
TypeSymbol patternType,
IdentifierNameSyntax memberName,
......@@ -275,29 +274,36 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node,
switch (boundMember.Kind)
{
case BoundKind.FieldAccess:
case BoundKind.EventAccess:
case BoundKind.PropertyAccess:
break;
case BoundKind.IndexerAccess:
{
var indexer = (BoundIndexerAccess)boundMember;
arguments = indexer.Arguments;
argumentNamesOpt = indexer.ArgumentNamesOpt;
argsToParamsOpt = indexer.ArgsToParamsOpt;
argumentRefKindsOpt = indexer.ArgumentRefKindsOpt;
expanded = indexer.Expanded;
break;
}
// TODO: Should a property pattern be capable of referencing an indexed property?
// https://github.com/dotnet/roslyn/issues/9375
//{
// var indexer = (BoundIndexerAccess)boundMember;
// arguments = indexer.Arguments;
// argumentNamesOpt = indexer.ArgumentNamesOpt;
// argsToParamsOpt = indexer.ArgsToParamsOpt;
// argumentRefKindsOpt = indexer.ArgumentRefKindsOpt;
// expanded = indexer.Expanded;
// break;
//}
case BoundKind.DynamicIndexerAccess:
{
var indexer = (BoundDynamicIndexerAccess)boundMember;
arguments = indexer.Arguments;
argumentNamesOpt = indexer.ArgumentNamesOpt;
argumentRefKindsOpt = indexer.ArgumentRefKindsOpt;
break;
}
// TODO: Should a property pattern be capable of referencing a dynamic indexer?
// https://github.com/dotnet/roslyn/issues/9375
//{
// var indexer = (BoundDynamicIndexerAccess)boundMember;
// arguments = indexer.Arguments;
// argumentNamesOpt = indexer.ArgumentNamesOpt;
// argumentRefKindsOpt = indexer.ArgumentRefKindsOpt;
// break;
//}
case BoundKind.EventAccess:
// TODO: Should a property pattern be capable of referencing an event?
// https://github.com/dotnet/roslyn/issues/9515
default:
return BadSubpatternMemberAccess(boundMember, implicitReceiver, memberName, diagnostics, hasErrors);
......@@ -349,44 +355,7 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node,
}
}
return ToBadExpression(boundMember, LookupResultKind.NotAValue);
}
private Symbol FindPropertyOrFieldByName(
TypeSymbol type,
SyntaxToken name,
out LookupResultKind resultKind,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
var symbols = ArrayBuilder<Symbol>.GetInstance();
var lookupResult = LookupResult.GetInstance();
this.LookupMembersWithFallback(
lookupResult, type, name.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics);
resultKind = lookupResult.Kind;
Symbol result = null;
if (lookupResult.IsMultiViable)
{
foreach (var symbol in lookupResult.Symbols)
{
if (symbol.Kind == SymbolKind.Property || symbol.Kind == SymbolKind.Field)
{
if (result != null && symbol != result)
{
resultKind = LookupResultKind.Ambiguous;
result = null;
break;
}
else
{
result = symbol;
}
}
}
}
lookupResult.Free();
return result;
return ToBadExpression(boundMember, LookupResultKind.Inaccessible);
}
private BoundPattern BindConstantPattern(
......
......@@ -50,14 +50,25 @@ BoundExpression TranslatePattern(BoundExpression input, BoundPattern pattern)
{
var subProperty = (pat.Subpatterns[i].Member as BoundPropertyPatternMember)?.MemberSymbol;
var subPattern = pat.Subpatterns[i].Pattern;
var subExpression =
subProperty?.Kind == SymbolKind.Field ?
_factory.Field(input, (FieldSymbol)subProperty) :
subProperty?.Kind == SymbolKind.Property ?
_factory.Call(input, ((PropertySymbol)subProperty).GetMethod) :
(BoundExpression)new BoundBadExpression(
_factory.Syntax, LookupResultKind.Inaccessible, ImmutableArray<Symbol>.Empty,
ImmutableArray<BoundNode>.Empty, pat.Type);
BoundExpression subExpression;
switch (subProperty?.Kind)
{
case SymbolKind.Field:
subExpression = _factory.Field(input, (FieldSymbol)subProperty);
break;
case SymbolKind.Property:
subExpression = _factory.Call(input, ((PropertySymbol)subProperty).GetMethod);
break;
case SymbolKind.Event:
// TODO: should a property pattern be capable of referencing an event?
// https://github.com/dotnet/roslyn/issues/9515
default:
subExpression = new BoundBadExpression(
_factory.Syntax, LookupResultKind.Inaccessible, ImmutableArray<Symbol>.Empty,
ImmutableArray<BoundNode>.Empty, pat.Type);
break;
}
var partialMatch = this.TranslatePattern(subExpression, subPattern);
matched = _factory.LogicalAnd(matched, partialMatch);
}
......
......@@ -53,10 +53,7 @@ private CSharpSyntaxNode ParseTypeOrPattern()
// X.Y.Z { ... } : PropertyPattern
else if (tk == SyntaxKind.OpenBraceToken)
{
var open = this.EatToken(SyntaxKind.OpenBraceToken);
var list = this.ParseSubPropertyPatternList(ref open);
var close = this.EatToken(SyntaxKind.CloseBraceToken);
node = _syntaxFactory.PropertyPattern(type, open, list, close);
node = ParsePropertyPatternBody(type);
}
// X.Y.Z id
else if (this.IsTrueIdentifier())
......@@ -99,6 +96,14 @@ private CSharpSyntaxNode ParseTypeOrPattern()
return node;
}
private PropertyPatternSyntax ParsePropertyPatternBody(TypeSyntax type)
{
var open = this.EatToken(SyntaxKind.OpenBraceToken);
var list = this.ParseSubPropertyPatternList(ref open);
var close = this.EatToken(SyntaxKind.CloseBraceToken);
return _syntaxFactory.PropertyPattern(type, open, list, close);
}
private SubRecursivePatternListSyntax ParseSubRecursivePatternList()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.SubRecursivePatternList)
......@@ -246,10 +251,7 @@ private CSharpSyntaxNode ParseExpressionOrPattern()
// X.Y.Z { ... } : PropertyPattern
else if (tk == SyntaxKind.OpenBraceToken)
{
var openBrace = this.EatToken(SyntaxKind.OpenBraceToken);
var contents = this.ParseSubPropertyPatternList(ref openBrace);
var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken);
node = _syntaxFactory.PropertyPattern(type, openBrace, contents, closeBrace);
node = ParsePropertyPatternBody(type);
}
// X.Y.Z id
else if (this.IsTrueIdentifier())
......
......@@ -1724,7 +1724,8 @@
</Field>
<Field Name="SubPatterns" Type="SeparatedSyntaxList&lt;ExpressionSyntax&gt;">
<PropertyComment>
<summary>SeparatedSyntaxList of ExpressionSyntax representing the list of "ID is Pattern" expressions in the property pattern.</summary>
<summary>
The list of "ID is Pattern" expressions in the property pattern represented as IsPatternExpressionSyntax nodes.</summary>
</PropertyComment>
</Field>
<Field Name="CloseBraceToken" Type="SyntaxToken">
......
......@@ -1042,14 +1042,16 @@ public static void Main()
object o = nameof(Main);
Console.WriteLine(o is P { Good is 4 });
Console.WriteLine(o is P { NotFound is 4 });
Console.WriteLine(o is P { Unreadable is 4 });
Console.WriteLine(o is P { Unreadable1 is 4 });
Console.WriteLine(o is P { Unreadable2 is 4 });
}
}
class P
{
public int Good = 2;
public int Unreadable { set { } }
public int Unreadable1 { set { } }
public int Unreadable2 { set { } protected get { return 0; } }
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions);
......@@ -1058,15 +1060,18 @@ class P
// (9,36): error CS0117: 'P' does not contain a definition for 'NotFound'
// Console.WriteLine(o is P { NotFound is 4 });
Diagnostic(ErrorCode.ERR_NoSuchMember, "NotFound").WithArguments("P", "NotFound").WithLocation(9, 36),
// (10,36): error CS0154: The property or indexer 'P.Unreadable' cannot be used in this context because it lacks the get accessor
// Console.WriteLine(o is P { Unreadable is 4 });
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "Unreadable").WithArguments("P.Unreadable").WithLocation(10, 36)
// (10,36): error CS0154: The property or indexer 'P.Unreadable1' cannot be used in this context because it lacks the get accessor
// Console.WriteLine(o is P { Unreadable1 is 4 });
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "Unreadable1").WithArguments("P.Unreadable1").WithLocation(10, 36),
// (11,36): error CS0271: The property or indexer 'P.Unreadable2' cannot be used in this context because the get accessor is inaccessible
// Console.WriteLine(o is P { Unreadable2 is 4 });
Diagnostic(ErrorCode.ERR_InaccessibleGetter, "Unreadable2").WithArguments("P.Unreadable2").WithLocation(11, 36)
);
var tree = compilation.SyntaxTrees.Single();
var model = compilation.GetSemanticModel(tree);
var propPats = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Where(e => e.Parent is PropertyPatternSyntax).ToArray();
Assert.Equal(3, propPats.Length);
Assert.Equal(4, propPats.Length);
var p = propPats[0].Expression; // 'Good' in Good is 4
var si = model.GetSymbolInfo(p);
......@@ -1081,11 +1086,19 @@ class P
Assert.Equal(CandidateReason.None, si.CandidateReason);
Assert.True(si.CandidateSymbols.IsDefaultOrEmpty);
p = propPats[2].Expression; // 'Unreadable' in Unreadable is 4
p = propPats[2].Expression; // 'Unreadable1' in Unreadable1 is 4
si = model.GetSymbolInfo(p);
Assert.Null(si.Symbol);
Assert.Equal(CandidateReason.NotAValue, si.CandidateReason);
Assert.Equal(1, si.CandidateSymbols.Length);
Assert.Equal("Unreadable1", si.CandidateSymbols[0].Name);
p = propPats[3].Expression; // 'Unreadable2' in Unreadable2 is 4
si = model.GetSymbolInfo(p);
Assert.Null(si.Symbol);
Assert.Equal(CandidateReason.NotAValue, si.CandidateReason);
Assert.Equal(1, si.CandidateSymbols.Length);
Assert.Equal("Unreadable2", si.CandidateSymbols[0].Name);
}
[Fact, WorkItem(9284, "https://github.com/dotnet/roslyn/issues/9284")]
......@@ -1129,6 +1142,8 @@ interface I3 : I1, I2 { }
Assert.Null(si.Symbol);
Assert.Equal(CandidateReason.Ambiguous, si.CandidateReason);
Assert.Equal(2, si.CandidateSymbols.Length);
Assert.Equal("I1", si.CandidateSymbols[0].ContainingSymbol.Name);
Assert.Equal("I2", si.CandidateSymbols[1].ContainingSymbol.Name);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/9284"), WorkItem(9284, "https://github.com/dotnet/roslyn/issues/9284")]
......@@ -1170,6 +1185,45 @@ class Point
Assert.Equal(CandidateReason.StaticInstanceMismatch, si.CandidateReason);
}
[Fact]
public void InaccessibleNamedProperty()
{
var source =
@"
using System;
public class Program
{
public static void Main()
{
object o = null;
Console.WriteLine(o is Point { X is 4 });
}
}
class Point
{
protected int X => 0;
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions);
// TODO: need a better diagnostic for this
compilation.VerifyDiagnostics(
// (8,40): error CS0122: 'Point.X' is inaccessible due to its protection level
// Console.WriteLine(o is Point { X is 4 });
Diagnostic(ErrorCode.ERR_BadAccess, "X").WithArguments("Point.X").WithLocation(8, 40)
);
var tree = compilation.SyntaxTrees.Single();
var model = compilation.GetSemanticModel(tree);
var propPats = tree.GetRoot().DescendantNodes().OfType<PropertyPatternSyntax>().SelectMany(s => s.SubPatterns).OfType<IsPatternExpressionSyntax>().ToArray();
Assert.Equal(1, propPats.Length);
var p = propPats[0].Expression; // 'X' in "X is 4"
var si = model.GetSymbolInfo(p);
Assert.Null(si.Symbol);
Assert.Equal(1, si.CandidateSymbols.Length);
Assert.Equal(CandidateReason.Inaccessible, si.CandidateReason);
}
private static void VerifyModelForDeclarationPattern(SemanticModel model, DeclarationPatternSyntax decl, params IdentifierNameSyntax[] references)
{
var symbol = model.GetDeclaredSymbol(decl);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册