Repository: AvaloniaUI/Avalonia

Issue:
Parsing binding expression with type cast of attached property fails
## Describe the bug
I'm attempting to bind to a property of the adorned element in a template set to the adorner property. To do this with compiled bindings a type cast is required but that fails to parse. 

## To Reproduce
```xml
  <TextBox>
    <TextBox.Styles>
      <Style Selector="TextBox">
        <Setter Property="(AdornerLayer.Adorner)">
          <Template>
            <TextBlock Margin="10 20 0 0" Foreground="Red" Text="{Binding $self.((TextBox)(AdornerLayer.AdornedElement)).Text}"/>
          </Template>
        </Setter>
      </Style>
    </TextBox.Styles>
  </TextBox>
```
This fails to compile. 
```
Avalonia error AVLN:0004: Internal compiler error while transforming node XamlX.Ast.XamlAstObjectNode:
Avalonia error AVLN:0004: Avalonia.Data.Core.ExpressionParseException: Expected ')'.
Avalonia error AVLN:0004:    at Avalonia.Markup.Parsers.BindingExpressionGrammar.ParseTypeCast(CharacterReader& r, List`1 nodes) in /_/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs:line 249
Avalonia error AVLN:0004:    at Avalonia.Markup.Parsers.BindingExpressionGrammar.Parse(CharacterReader& r) in /_/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs:line 50
Avalonia error AVLN:0004:    at Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers.AvaloniaXamlIlBindingPathParser.Transform(AstTransformationContext context, IXamlAstNode node) in /_/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs:line 28
Avalonia error AVLN:0004:    at XamlX.Transform.AstTransformationContext.Visitor.Visit(IXamlAstNode node) in /_/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/src/XamlX/Transform/AstTransformationContext.cs:line 58 Line 20, position 60.
```

With a ReflectionBinding it fails at runtime. 
```
Avalonia.Data.Core.ExpressionParseException
  HResult=0x80131500
  Message=Expected ')'.
  Source=Avalonia.Markup
  StackTrace:
   at Avalonia.Markup.Parsers.BindingExpressionGrammar.ParseTypeCast(CharacterReader& r, List`1 nodes)
   at Avalonia.Markup.Parsers.BindingExpressionGrammar.Parse(CharacterReader& r)
   at Avalonia.Markup.Parsers.ExpressionParser.Parse(CharacterReader& r)
   at Avalonia.Markup.Parsers.ExpressionObserverBuilder.Parse(String expression, Boolean enableValidation, Func`3 typeResolver, INameScope nameScope)
   at Avalonia.Data.Binding.CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty targetProperty, Object anchor, Boolean enableDataValidation)
   at Avalonia.Data.BindingBase.Initiate(AvaloniaObject target, AvaloniaProperty targetProperty, Object anchor, Boolean enableDataValidation)
   at Avalonia.AvaloniaObjectExtensions.Bind(AvaloniaObject target, AvaloniaProperty property, IBinding binding, Object anchor)
   at AvaloniaApplication3.MainWindow.XamlClosure_2.Build(IServiceProvider ) in MainWindow.axaml:line 20
   at Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers.<>c__DisplayClass1_0`1.<DeferredTransformationFactoryV2>b__0(IServiceProvider sp)
   at Avalonia.Markup.Xaml.Templates.TemplateContent.Load(Object templateContent)
   at Avalonia.Markup.Xaml.Templates.Template.Build()
   at Avalonia.Markup.Xaml.Templates.Template.Avalonia.Styling.ITemplate.Build()
   at Avalonia.Styling.PropertySetterTemplateInstance.GetValue()
   at Avalonia.PropertyStore.EffectiveValue`1.GetValue(IValueEntry entry)
   at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaise(ValueStore owner, IValueEntry value, BindingPriority priority)
   at Avalonia.PropertyStore.ValueStore.ReevaluateEffectiveValues(IValueEntry changedValueEntry)
   at Avalonia.PropertyStore.ValueStore.EndStyling()
   at Avalonia.StyledElement.ApplyStyling()
   at Avalonia.StyledElement.EndInit()
   at AvaloniaApplication3.MainWindow.!XamlIlPopulate(IServiceProvider , MainWindow ) in MainWindow.axaml:line 14
   at AvaloniaApplication3.MainWindow.!XamlIlPopulateTrampoline(MainWindow )
   at AvaloniaApplication3.MainWindow.InitializeComponent(Boolean loadXaml, Boolean attachDevTools) in Avalonia.Generators\Avalonia.Generators.NameGenerator.AvaloniaNameSourceGenerator\AvaloniaApplication3.MainWindow.g.cs:line 23
   at AvaloniaApplication3.MainWindow..ctor() in MainWindow.axaml.cs:line 14
   at AvaloniaApplication3.App.OnFrameworkInitializationCompleted() in App.axaml.cs:line 18
   at Avalonia.AppBuilder.SetupUnsafe()
   at Avalonia.AppBuilder.Setup()
   at Avalonia.AppBuilder.SetupWithLifetime(IApplicationLifetime lifetime)
   at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, ShutdownMode shutdownMode)
   at AvaloniaApplication3.Program.Main(String[] args) in Program.cs:line 12
```

With ReflectionBinding you don't need the type cast so if you instead do `{ReflectionBinding $self.(AdornerLayer.AdornedElement).Text}` it works. 

## Expected behavior
No error and successful binding the same as when using the ReflectionBinding without type cast. 

## Environment:

- OS: Windows
- Avalonia-Version: 11.0.5

## Additional context
It looks like BindingExpressionGrammar.ParseTypeCast expects ParseBeforeMember to parse the identifier but for attached properties it doesn't but instead returns State.AttachedProperty. 

If I add parsing of an attached property after the call to ParseBeforeMember it looks like it works but I'm not well versed in the code base enough to know if that would be enough. 

```csharp
if (parseMemberBeforeAddCast)
{
	if (!ParseCloseBrace(ref r))
	{
		throw new ExpressionParseException(r.Position, "Expected ')'.");
	}

	result = ParseBeforeMember(ref r, nodes);

	// With this parsing a type cast of an attached property seems to work
	if (result == State.AttachedProperty)
		result = ParseAttachedProperty(ref r, nodes);

	if(r.Peek == '[')
	{
		result = ParseIndexer(ref r, nodes);
	}
}
```

```csharp
        [Fact]
        public async Task Should_Get_Chained_Attached_Property_Value_With_TypeCast()
        {
            var expected = new Class1();

            var data = new Class1();
            data.SetValue(Owner.SomethingProperty, new Class1() { Next = expected });

            var target = ExpressionObserverBuilder.Build(data, "((Class1)(Owner.Something)).Next", typeResolver: (ns, name) => name == "Class1" ? typeof(Class1) : _typeResolver(ns, name));
            var result = await target.Take(1);

            Assert.Equal(expected, result);

            Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers());
        }

        private static class Owner
        {
            public static readonly AttachedProperty<string> FooProperty =
                AvaloniaProperty.RegisterAttached<Class1, string>(
                    "Foo", 
                    typeof(Owner), 
                    defaultValue: "foo");

            public static readonly AttachedProperty<AvaloniaObject> SomethingProperty =
                AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject>(
                    "Something",
                    typeof(Owner));
        }
```


Hints:
@appel1 great findings and I like that you also directly added a unit test. If you want to, you may try to make a PR out of it. 

This may help: 
- https://github.com/AvaloniaUI/Avalonia/wiki/Debugging-the-XAML-compiler
- https://github.com/AvaloniaUI/Avalonia/blob/master/Documentation/build.md 


Patch:
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
index 5a4a56b7469..179ba009c02 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
@@ -257,8 +257,10 @@ private static State ParseTypeCast(ref CharacterReader r, List<INode> nodes)
                 }
 
                 result = ParseBeforeMember(ref r, nodes);
+                if (result == State.AttachedProperty)
+                    result = ParseAttachedProperty(ref r, nodes);
 
-                if(r.Peek == '[')
+                if (r.Peek == '[')
                 {
                     result = ParseIndexer(ref r, nodes);
                 }


Test patch:
diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
index 4c9333c42b3..cc2dc35f0bf 100644
--- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
+++ b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
@@ -65,6 +65,22 @@ public async Task Should_Get_Chained_Attached_Property_Value()
             Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers());
         }
 
+        [Fact]
+        public async Task Should_Get_Chained_Attached_Property_Value_With_TypeCast()
+        {
+            var expected = new Class1();
+
+            var data = new Class1();
+            data.SetValue(Owner.SomethingProperty, new Class1() { Next = expected });
+
+            var target = Build(data, "((Class1)(Owner.Something)).Next", typeResolver: (ns, name) => name == "Class1" ? typeof(Class1) : _typeResolver(ns, name));
+            var result = await target.Take(1);
+
+            Assert.Equal(expected, result);
+
+            Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers());
+        }
+
         [Fact]
         public void Should_Track_Simple_Attached_Value()
         {
@@ -159,6 +175,11 @@ private static class Owner
                     "Foo",
                     typeof(Owner),
                     defaultValue: "foo");
+
+            public static readonly AttachedProperty<AvaloniaObject> SomethingProperty =
+                AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject>(
+                    "Something",
+                    typeof(Owner));
         }
 
         private class Class1 : AvaloniaObject