From e54a93e8520517a3f005febb3b780fdb6c7feac2 Mon Sep 17 00:00:00 2001 From: Adriano Carlos Verona Date: Wed, 19 Jun 2024 10:07:21 -0400 Subject: [PATCH] fixes crahes with some recursive patterns (#281) --- .../Tests/Unit/PatternExpressionTests.cs | 37 ++++++++++++++++--- Cecilifier.Core/AST/ExpressionVisitor.cs | 4 +- Cecilifier.Core/AST/SyntaxWalkerBase.cs | 2 +- .../Extensions/SyntaxNodeExtensions.cs | 11 +++--- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Cecilifier.Core.Tests/Tests/Unit/PatternExpressionTests.cs b/Cecilifier.Core.Tests/Tests/Unit/PatternExpressionTests.cs index d0229b8d..4d0faa3e 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/PatternExpressionTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/PatternExpressionTests.cs @@ -119,11 +119,36 @@ private static IEnumerable RecursivePatternsTestScenarios() { TestName = "Multiple Properties" }; // Issue #281 - // yield return new TestCaseData( - // "void M(object o) { var r = o is System.Uri { Host.Length: 10 }; }", - // "TODO: //DEFINE EXPECTATION") - // { - // TestName = "Property (MemberAccessExpression)" - // }; + yield return new TestCaseData( + "void M(object o) { var r = o is System.Uri { Host.Length: 10 }; }", + """ + //var r = o is System.Uri { Host.Length: 10 }; + \s+var l_r_\d+ = new VariableDefinition\(assembly.MainModule.TypeSystem.Boolean\); + \s+m_M_\d+.Body.Variables.Add\(l_r_\d+\); + \s+il_M_\d+.Emit\(OpCodes.Ldarg_1\); + \s+var l_tmp_\d+ = new VariableDefinition\(assembly.MainModule.ImportReference\(typeof\(System.Uri\)\)\); + \s+m_M_\d+.Body.Variables.Add\(l_tmp_\d+\); + \s+var ldc_I4_0_\d+ = il_M_\d+.Create\(OpCodes.Ldc_I4_0\); + \s+var nop_\d+ = il_M_\d+.Create\(OpCodes.Nop\); + (\s+il_M_\d+\.Emit\(OpCodes\.)Isinst, assembly.MainModule.ImportReference\(typeof\(System.Uri\)\)\); + \1Stloc, l_tmp_\d+\); + \1Ldloc, l_tmp_\d+\); + \1Brfalse_S, ldc_I4_0_\d+\); + \1Ldloc, l_tmp_\d+\); + \1Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Uri\), "get_Host",.+\)\)\); + \1Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.String\), "get_Length",.+\)\)\); + \1Ldc_I4, 10\); + \1Bne_Un, ldc_I4_0_\d+\); + \1Ldc_I4_1\); + \1Br_S, nop_\d+\); + \s+il_M_\d+.Append\(ldc_I4_0_\d+\); + \s+il_M_\d+.Append\(nop_\d+\); + \1Stloc, l_r_\d+\); + \1Ret\); + \s+//End of local function. + """) + { + TestName = "Property (MemberAccessExpression)" + }; } } diff --git a/Cecilifier.Core/AST/ExpressionVisitor.cs b/Cecilifier.Core/AST/ExpressionVisitor.cs index c73b24a2..25962d01 100644 --- a/Cecilifier.Core/AST/ExpressionVisitor.cs +++ b/Cecilifier.Core/AST/ExpressionVisitor.cs @@ -871,7 +871,7 @@ public override void VisitRecursivePattern(RecursivePatternSyntax node) Context.EmitCilInstruction(ilVar, OpCodes.Ldloc, localVar); pattern.Accept(this); - var comparisonType = Context.SemanticModel.GetSymbolInfo(pattern.NameColon.Name).Symbol.GetMemberType(); + var comparisonType = Context.SemanticModel.GetSymbolInfo(pattern.NameColon?.Name ?? pattern.ExpressionColon?.Expression).Symbol.GetMemberType(); var opEquality = comparisonType.GetMembers().FirstOrDefault(m => m.Kind == SymbolKind.Method && m.Name == "op_Equality"); if (opEquality != null) { @@ -1231,7 +1231,7 @@ private void ProcessProperty(SimpleNameSyntax node, IPropertySymbol propertySymb { propertySymbol.EnsurePropertyExists(Context, node); - if (!propertySymbol.IsStatic && !node.IsQualifiedAccessToTypeOrMember()) + if (!propertySymbol.IsStatic && node.IsMemberAccessThroughImplicitThis()) { // if this is an *unqualified* access we need to load *this* Context.EmitCilInstruction(ilVar, OpCodes.Ldarg_0); diff --git a/Cecilifier.Core/AST/SyntaxWalkerBase.cs b/Cecilifier.Core/AST/SyntaxWalkerBase.cs index 7c74d7d2..10c33337 100644 --- a/Cecilifier.Core/AST/SyntaxWalkerBase.cs +++ b/Cecilifier.Core/AST/SyntaxWalkerBase.cs @@ -466,7 +466,7 @@ protected void ProcessField(string ilVar, SimpleNameSyntax node, IFieldSymbol fi var fieldDeclarationVariable = fieldSymbol.EnsureFieldExists(Context, node); - if (!fieldSymbol.IsStatic && !node.IsQualifiedAccessToTypeOrMember()) + if (!fieldSymbol.IsStatic && node.IsMemberAccessThroughImplicitThis()) Context.EmitCilInstruction(ilVar, OpCodes.Ldarg_0); if (HandleLoadAddressOnStorage(ilVar, fieldSymbol.Type, node, fieldSymbol.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fieldSymbol.Name, VariableMemberKind.Field, fieldSymbol.ContainingType.ToDisplayString())) diff --git a/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs b/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs index b27cafd4..465f3246 100644 --- a/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs +++ b/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs @@ -23,12 +23,13 @@ public static class SyntaxNodeExtensions /// 1. in, `Foo f = new NS.Foo();`, `Foo` : Foo f => Unqualified, NS.Foo => Qualified /// 2. `o.field ?? otherField`, otherField => Unqualified, `field` in `o.field` => Qualified /// - public static bool IsQualifiedAccessToTypeOrMember(this SimpleNameSyntax node) => node.Parent switch + public static bool IsMemberAccessThroughImplicitThis(this SyntaxNode node) => node.Parent switch { - MemberAccessExpressionSyntax mae => mae.Name == node, - MemberBindingExpressionSyntax mbe => mbe.Name == node, // A MemberBindExpression represents `?.` in the null conditional operator, for instance, `o?.member` - NameColonSyntax => true, // A NameColon syntax represents the `Length: 42` in an expression like `o as string { Length: 42 }`. In this case, `Length` is equivalent to `o.Length` - _ => false + MemberAccessExpressionSyntax mae => mae.Name != node && mae.IsMemberAccessThroughImplicitThis(), + MemberBindingExpressionSyntax mbe => mbe.Name != node, // A MemberBindExpression represents `?.` in the null conditional operator, for instance, `o?.member` + NameColonSyntax => false, // A NameColon syntax represents the `Length: 42` in an expression like `o as string { Length: 42 }`. In this case, `Length` is equivalent to `o.Length` + ExpressionColonSyntax => false, // `o as Uri { Host.Length : 10 }`. Parent of `Host.Length` (which is equivalent to `o.Host.Length`) is an ExpressionColonSyntax + _ => true }; ///