Browse Source

Merge pull request #5326 from jp2masa/nullable

Enabled nullable reference types on Avalonia.Markup
pull/5462/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
73ad36cbec
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  2. 49
      src/Markup/Avalonia.Markup/Data/Binding.cs
  3. 49
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  4. 14
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  5. 2
      src/Markup/Avalonia.Markup/Data/RelativeSource.cs
  6. 17
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  7. 26
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  8. 21
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  9. 22
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  10. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
  11. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs
  12. 16
      src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs
  13. 36
      src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs
  14. 42
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  15. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

2
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
<Nullable>Enable</Nullable>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Remove="Markup\Parsers\Nodes\ExpressionGrammer" />

49
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -39,17 +39,17 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the name of the element to use as the binding source.
/// </summary>
public string ElementName { get; set; }
public string? ElementName { get; set; }
/// <summary>
/// Gets or sets the relative source for the binding.
/// </summary>
public RelativeSource RelativeSource { get; set; }
public RelativeSource? RelativeSource { get; set; }
/// <summary>
/// Gets or sets the source for the binding.
/// </summary>
public object Source { get; set; }
public object? Source { get; set; }
/// <summary>
/// Gets or sets the binding path.
@ -59,24 +59,36 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets a function used to resolve types from names in the binding path.
/// </summary>
public Func<string, string, Type> TypeResolver { get; set; }
public Func<string, string, Type>? TypeResolver { get; set; }
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object? anchor, bool enableDataValidation)
{
Contract.Requires<ArgumentNullException>(target != null);
anchor = anchor ?? DefaultAnchor?.Target;
_ = target ?? throw new ArgumentNullException(nameof(target));
anchor ??= DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
INameScope nameScope = null;
INameScope? nameScope = null;
NameScope?.TryGetTarget(out nameScope);
var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver, nameScope);
if (node is null)
{
throw new InvalidOperationException("Could not parse binding expression.");
}
IStyledElement GetSource()
{
return target as IStyledElement ??
anchor as IStyledElement ??
throw new ArgumentException("Could not find binding source: either target or anchor must be an IStyledElement.");
}
if (ElementName != null)
{
return CreateElementObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
GetSource(),
ElementName,
node);
}
@ -96,9 +108,7 @@ namespace Avalonia.Data
}
else
{
return CreateSourceObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateSourceObserver(GetSource(), node);
}
}
else if (RelativeSource.Mode == RelativeSourceMode.DataContext)
@ -111,15 +121,11 @@ namespace Avalonia.Data
}
else if (RelativeSource.Mode == RelativeSourceMode.Self)
{
return CreateSourceObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateSourceObserver(GetSource(), node);
}
else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent)
{
return CreateTemplatedParentObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
node);
return CreateTemplatedParentObserver(GetSource(), node);
}
else if (RelativeSource.Mode == RelativeSourceMode.FindAncestor)
{
@ -128,10 +134,7 @@ namespace Avalonia.Data
throw new InvalidOperationException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.");
}
return CreateFindAncestorObserver(
(target as IStyledElement) ?? (anchor as IStyledElement),
RelativeSource,
node);
return CreateFindAncestorObserver(GetSource(), RelativeSource, node);
}
else
{

49
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@ -38,22 +38,22 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// </summary>
public IValueConverter Converter { get; set; }
public IValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to produce a value.
/// </summary>
public object FallbackValue { get; set; }
public object? FallbackValue { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding result is null.
/// </summary>
public object TargetNullValue { get; set; }
public object? TargetNullValue { get; set; }
/// <summary>
/// Gets or sets the binding mode.
@ -68,26 +68,27 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
public string? StringFormat { get; set; }
public WeakReference DefaultAnchor { get; set; }
public WeakReference? DefaultAnchor { get; set; }
public WeakReference<INameScope> NameScope { get; set; }
public WeakReference<INameScope>? NameScope { get; set; }
protected abstract ExpressionObserver CreateExpressionObserver(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor,
object? anchor,
bool enableDataValidation);
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
anchor = anchor ?? DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
@ -133,18 +134,13 @@ namespace Avalonia.Data
IAvaloniaObject target,
ExpressionNode node,
bool targetIsDataContext,
object anchor)
object? anchor)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
if (!(target is IDataContextProvider))
{
target = anchor as IDataContextProvider;
if (target == null)
{
throw new InvalidOperationException("Cannot find a DataContext to bind to.");
}
target = anchor as IDataContextProvider ?? throw new InvalidOperationException("Cannot find a DataContext to bind to.");
}
if (!targetIsDataContext)
@ -171,10 +167,9 @@ namespace Avalonia.Data
string elementName,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
NameScope.TryGetTarget(out var scope);
if (scope == null)
if (NameScope is null || !NameScope.TryGetTarget(out var scope) || scope is null)
throw new InvalidOperationException("Name scope is null or was already collected");
var result = new ExpressionObserver(
NameScopeLocator.Track(scope, elementName),
@ -188,9 +183,9 @@ namespace Avalonia.Data
RelativeSource relativeSource,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
IObservable<object> controlLocator;
IObservable<object?> controlLocator;
switch (relativeSource.Tree)
{
@ -220,7 +215,7 @@ namespace Avalonia.Data
object source,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(source != null);
_ = source ?? throw new ArgumentNullException(nameof(source));
return new ExpressionObserver(source, node);
}
@ -229,7 +224,7 @@ namespace Avalonia.Data
IAvaloniaObject target,
ExpressionNode node)
{
Contract.Requires<ArgumentNullException>(target != null);
_ = target ?? throw new ArgumentNullException(nameof(target));
var result = new ExpressionObserver(
() => target.GetValue(StyledElement.TemplatedParentProperty),
@ -240,7 +235,7 @@ namespace Avalonia.Data
return result;
}
protected IObservable<object> GetParentDataContext(IAvaloniaObject target)
protected IObservable<object?> GetParentDataContext(IAvaloniaObject target)
{
// The DataContext is based on the visual parent and not the logical parent: this may
// seem counter intuitive considering the fact that property inheritance works on the logical
@ -252,7 +247,7 @@ namespace Avalonia.Data
.Select(x =>
{
return (x as IAvaloniaObject)?.GetObservable(StyledElement.DataContextProperty) ??
Observable.Return((object)null);
Observable.Return((object?)null);
}).Switch();
}

14
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -22,12 +22,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IMultiValueConverter"/> to use.
/// </summary>
public IMultiValueConverter Converter { get; set; }
public IMultiValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to produce a value.
@ -52,12 +52,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the relative source for the binding.
/// </summary>
public RelativeSource RelativeSource { get; set; }
public RelativeSource? RelativeSource { get; set; }
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
public string? StringFormat { get; set; }
public MultiBinding()
{
@ -69,7 +69,7 @@ namespace Avalonia.Data
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
var targetType = targetProperty?.PropertyType ?? typeof(object);
@ -105,7 +105,7 @@ namespace Avalonia.Data
}
}
private object ConvertValue(IList<object> values, Type targetType, IMultiValueConverter converter)
private object ConvertValue(IList<object?> values, Type targetType, IMultiValueConverter? converter)
{
for (var i = 0; i < values.Count; ++i)
{
@ -116,7 +116,7 @@ namespace Avalonia.Data
}
var culture = CultureInfo.CurrentCulture;
values = new System.Collections.ObjectModel.ReadOnlyCollection<object>(values);
values = new System.Collections.ObjectModel.ReadOnlyCollection<object?>(values);
object converted;
if (converter != null)
{

2
src/Markup/Avalonia.Markup/Data/RelativeSource.cs

@ -94,7 +94,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets the type of ancestor to look for when in <see cref="RelativeSourceMode.FindAncestor"/> mode.
/// </summary>
public Type AncestorType { get; set; }
public Type? AncestorType { get; set; }
/// <summary>
/// Gets or sets a value that describes the type of relative source lookup.

17
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -17,8 +17,8 @@ namespace Avalonia.Data
ISetterValue
{
private bool _isSetterValue;
private IStyledElement _target;
private Type _targetType;
private IStyledElement _target = default!;
private Type? _targetType;
public TemplateBinding()
{
@ -33,7 +33,7 @@ namespace Avalonia.Data
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
object? anchor = null,
bool enableDataValidation = false)
{
// Usually each `TemplateBinding` will only be instantiated once; in this case we can
@ -68,12 +68,12 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// </summary>
public IValueConverter Converter { get; set; }
public IValueConverter? Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
public object? ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the binding mode.
@ -83,7 +83,7 @@ namespace Avalonia.Data
/// <summary>
/// Gets or sets the name of the source property on the templated parent.
/// </summary>
public AvaloniaProperty Property { get; set; }
public AvaloniaProperty? Property { get; set; }
/// <inheritdoc/>
public string Description => "TemplateBinding: " + Property;
@ -164,10 +164,7 @@ namespace Avalonia.Data
{
if (e.Property == StyledElement.TemplatedParentProperty)
{
var oldValue = (IAvaloniaObject)e.OldValue;
var newValue = (IAvaloniaObject)e.OldValue;
if (oldValue != null)
if (e.OldValue is IAvaloniaObject oldValue)
{
oldValue.PropertyChanged -= TemplatedParentPropertyChanged;
}

26
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@ -6,6 +6,8 @@ using Avalonia.Utilities;
using System;
using System.Collections.Generic;
#nullable enable
namespace Avalonia.Markup.Parsers
{
internal enum SourceMode
@ -271,8 +273,8 @@ namespace Avalonia.Markup.Parsers
}
else if (mode.SequenceEqual("parent".AsSpan()))
{
string ancestorNamespace = null;
string ancestorType = null;
string? ancestorNamespace = null;
string? ancestorType = null;
var ancestorLevel = 0;
if (PeekOpenBracket(ref r))
{
@ -424,19 +426,19 @@ namespace Avalonia.Markup.Parsers
public class PropertyNameNode : INode
{
public string PropertyName { get; set; }
public string PropertyName { get; set; } = string.Empty;
}
public class AttachedPropertyNameNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string PropertyName { get; set; }
public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
public string PropertyName { get; set; } = string.Empty;
}
public class IndexerNode : INode
{
public IList<string> Arguments { get; set; }
public IList<string> Arguments { get; set; } = Array.Empty<string>();
}
public class NotNode : INode, ITransformNode { }
@ -447,20 +449,20 @@ namespace Avalonia.Markup.Parsers
public class NameNode : INode
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
}
public class AncestorNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string? Namespace { get; set; }
public string? TypeName { get; set; }
public int Level { get; set; }
}
public class TypeCastNode : INode
{
public string Namespace { get; set; }
public string TypeName { get; set; }
public string Namespace { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
}
}
}

21
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@ -8,8 +8,8 @@ namespace Avalonia.Markup.Parsers
{
public static class ExpressionObserverBuilder
{
internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type> typeResolver = null,
INameScope nameScope = null)
internal static (ExpressionNode? Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,
INameScope? nameScope = null)
{
if (string.IsNullOrWhiteSpace(expression))
{
@ -32,8 +32,8 @@ namespace Avalonia.Markup.Parsers
object root,
string expression,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
return new ExpressionObserver(
root,
@ -45,10 +45,11 @@ namespace Avalonia.Markup.Parsers
IObservable<object> rootObservable,
string expression,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
Contract.Requires<ArgumentNullException>(rootObservable != null);
_ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
return new ExpressionObserver(
rootObservable,
Parse(expression, enableDataValidation, typeResolver).Node,
@ -61,10 +62,10 @@ namespace Avalonia.Markup.Parsers
string expression,
IObservable<Unit> update,
bool enableDataValidation = false,
string description = null,
Func<string, string, Type> typeResolver = null)
string? description = null,
Func<string, string, Type>? typeResolver = null)
{
Contract.Requires<ArgumentNullException>(rootGetter != null);
_ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
return new ExpressionObserver(
rootGetter,

22
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@ -11,25 +11,25 @@ namespace Avalonia.Markup.Parsers
internal class ExpressionParser
{
private readonly bool _enableValidation;
private readonly Func<string, string, Type> _typeResolver;
private readonly INameScope _nameScope;
private readonly Func<string, string, Type>? _typeResolver;
private readonly INameScope? _nameScope;
public ExpressionParser(bool enableValidation, Func<string, string, Type> typeResolver, INameScope nameScope)
public ExpressionParser(bool enableValidation, Func<string, string, Type>? typeResolver, INameScope? nameScope)
{
_typeResolver = typeResolver;
_nameScope = nameScope;
_enableValidation = enableValidation;
}
public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r)
public (ExpressionNode? Node, SourceMode Mode) Parse(ref CharacterReader r)
{
ExpressionNode rootNode = null;
ExpressionNode node = null;
ExpressionNode? rootNode = null;
ExpressionNode? node = null;
var (astNodes, mode) = BindingExpressionGrammar.Parse(ref r);
foreach (var astNode in astNodes)
{
ExpressionNode nextNode = null;
ExpressionNode? nextNode = null;
switch (astNode)
{
case BindingExpressionGrammar.EmptyExpressionNode _:
@ -57,13 +57,13 @@ namespace Avalonia.Markup.Parsers
nextNode = ParseFindAncestor(ancestor);
break;
case BindingExpressionGrammar.NameNode elementName:
nextNode = new ElementNameNode(_nameScope, elementName.Name);
nextNode = new ElementNameNode(_nameScope ?? throw new NotSupportedException("Invalid element name binding with null name scope!"), elementName.Name);
break;
case BindingExpressionGrammar.TypeCastNode typeCast:
nextNode = ParseTypeCastNode(typeCast);
break;
}
if (rootNode is null)
if (node is null)
{
rootNode = node = nextNode;
}
@ -79,7 +79,7 @@ namespace Avalonia.Markup.Parsers
private FindAncestorNode ParseFindAncestor(BindingExpressionGrammar.AncestorNode node)
{
Type ancestorType = null;
Type? ancestorType = null;
var ancestorLevel = node.Level;
if (!(node.Namespace is null) && !(node.TypeName is null))
@ -97,7 +97,7 @@ namespace Avalonia.Markup.Parsers
private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
{
Type castType = null;
Type? castType = null;
if (!(node.Namespace is null) && !(node.TypeName is null))
{
if (_typeResolver == null)

2
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Markup.Parsers.Nodes
{
private readonly WeakReference<INameScope> _nameScope;
private readonly string _name;
private IDisposable _subscription;
private IDisposable? _subscription;
public ElementNameNode(INameScope nameScope, string name)
{

6
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs

@ -7,10 +7,10 @@ namespace Avalonia.Markup.Parsers.Nodes
public class FindAncestorNode : ExpressionNode
{
private readonly int _level;
private readonly Type _ancestorType;
private IDisposable _subscription;
private readonly Type? _ancestorType;
private IDisposable? _subscription;
public FindAncestorNode(Type ancestorType, int level)
public FindAncestorNode(Type? ancestorType, int level)
{
_level = level;
_ancestorType = ancestorType;

16
src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs

@ -29,15 +29,15 @@ namespace Avalonia.Markup.Parsers.Nodes
var list = target as IList;
var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo);
var indexerParameters = indexerProperty?.GetIndexParameters();
ParameterInfo[] indexerParameters;
if (indexerProperty != null && indexerParameters.Length == Arguments.Count)
if (indexerProperty != null && (indexerParameters = indexerProperty.GetIndexParameters()).Length == Arguments.Count)
{
var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++)
{
object temp = null;
object? temp = null;
if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp))
{
@ -125,7 +125,7 @@ namespace Avalonia.Markup.Parsers.Nodes
public IList<string> Arguments { get; }
public override Type PropertyType
public override Type? PropertyType
{
get
{
@ -144,15 +144,15 @@ namespace Avalonia.Markup.Parsers.Nodes
var list = target as IList;
var dictionary = target as IDictionary;
var indexerProperty = GetIndexer(typeInfo);
var indexerParameters = indexerProperty?.GetIndexParameters();
ParameterInfo[] indexerParameters;
if (indexerProperty != null && indexerParameters.Length == Arguments.Count)
if (indexerProperty != null && (indexerParameters = indexerProperty.GetIndexParameters()).Length == Arguments.Count)
{
var convertedObjectArray = new object[indexerParameters.Length];
for (int i = 0; i < Arguments.Count; i++)
{
object temp = null;
object? temp = null;
if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp))
{
@ -246,7 +246,7 @@ namespace Avalonia.Markup.Parsers.Nodes
return true;
}
private static PropertyInfo GetIndexer(TypeInfo typeInfo)
private static PropertyInfo? GetIndexer(TypeInfo? typeInfo)
{
PropertyInfo indexer;

36
src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using Avalonia.Data.Core;
using Avalonia.Utilities;
#nullable enable
namespace Avalonia.Markup.Parsers
{
#if !BUILDTASK
@ -30,7 +32,7 @@ namespace Avalonia.Markup.Parsers
var parsed = new List<ISyntax>();
while (state != State.End)
{
ISyntax syntax = null;
ISyntax? syntax = null;
if (state == State.Start)
(state, syntax) = ParseStart(ref r);
else if (state == State.Next)
@ -53,7 +55,7 @@ namespace Avalonia.Markup.Parsers
return parsed;
}
private static (State, ISyntax) ParseNext(ref CharacterReader r)
private static (State, ISyntax?) ParseNext(ref CharacterReader r)
{
r.SkipWhitespace();
if (r.End)
@ -106,7 +108,7 @@ namespace Avalonia.Markup.Parsers
});
}
static (string ns, string name) ParseXamlIdentifier(ref CharacterReader r)
static (string? ns, string name) ParseXamlIdentifier(ref CharacterReader r)
{
var ident = r.ParseIdentifier();
if (ident.IsEmpty)
@ -147,7 +149,7 @@ namespace Avalonia.Markup.Parsers
return true;
}
private static (State, ISyntax) ParseAfterProperty(ref CharacterReader r)
private static (State, ISyntax?) ParseAfterProperty(ref CharacterReader r)
{
if (TryParseCasts(ref r, out var rv))
return rv;
@ -184,20 +186,20 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is PropertySyntax other
&& other.Name == Name;
}
public class TypeQualifiedPropertySyntax : ISyntax
{
public string Name { get; set; }
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public string Name { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is TypeQualifiedPropertySyntax other
&& other.Name == Name
&& other.TypeName == TypeName
@ -207,14 +209,14 @@ namespace Avalonia.Markup.Parsers
public class ChildTraversalSyntax : ISyntax
{
public static ChildTraversalSyntax Instance { get; } = new ChildTraversalSyntax();
public override bool Equals(object obj) => obj is ChildTraversalSyntax;
public override bool Equals(object? obj) => obj is ChildTraversalSyntax;
}
public class EnsureTypeSyntax : ISyntax
{
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public override bool Equals(object obj)
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object? obj)
=> obj is EnsureTypeSyntax other
&& other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace;
@ -222,9 +224,9 @@ namespace Avalonia.Markup.Parsers
public class CastTypeSyntax : ISyntax
{
public string TypeName { get; set; }
public string TypeNamespace { get; set; }
public override bool Equals(object obj)
public string TypeName { get; set; } = string.Empty;
public string? TypeNamespace { get; set; }
public override bool Equals(object? obj)
=> obj is CastTypeSyntax other
&& other.TypeName == TypeName
&& other.TypeNamespace == TypeNamespace;

42
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

@ -4,6 +4,8 @@ using System.Linq;
using Avalonia.Data.Core;
using Avalonia.Utilities;
#nullable enable
// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the
// only reason they have overridden Equals methods is for unit testing.
#pragma warning disable 659
@ -39,7 +41,7 @@ namespace Avalonia.Markup.Parsers
var selector = new List<ISyntax>();
while (!r.End && state != State.End)
{
ISyntax syntax = null;
ISyntax? syntax = null;
switch (state)
{
case State.Start:
@ -110,7 +112,7 @@ namespace Avalonia.Markup.Parsers
return State.TypeName;
}
private static (State, ISyntax) ParseMiddle(ref CharacterReader r, char? end)
private static (State, ISyntax?) ParseMiddle(ref CharacterReader r, char? end)
{
if (r.TakeIf(':'))
{
@ -190,7 +192,7 @@ namespace Avalonia.Markup.Parsers
}
}
private static (State, ISyntax) ParseTraversal(ref CharacterReader r)
private static (State, ISyntax?) ParseTraversal(ref CharacterReader r)
{
r.SkipWhitespace();
if (r.TakeIf('>'))
@ -325,11 +327,11 @@ namespace Avalonia.Markup.Parsers
public class OfTypeSyntax : ISyntax, ITypeSyntax
{
public string TypeName { get; set; }
public string TypeName { get; set; } = string.Empty;
public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as OfTypeSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -338,11 +340,11 @@ namespace Avalonia.Markup.Parsers
public class IsSyntax : ISyntax, ITypeSyntax
{
public string TypeName { get; set; }
public string TypeName { get; set; } = string.Empty;
public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as IsSyntax;
return other != null && other.TypeName == TypeName && other.Xmlns == Xmlns;
@ -351,9 +353,9 @@ namespace Avalonia.Markup.Parsers
public class ClassSyntax : ISyntax
{
public string Class { get; set; }
public string Class { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is ClassSyntax && ((ClassSyntax)obj).Class == Class;
}
@ -361,9 +363,9 @@ namespace Avalonia.Markup.Parsers
public class NameSyntax : ISyntax
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is NameSyntax && ((NameSyntax)obj).Name == Name;
}
@ -371,11 +373,11 @@ namespace Avalonia.Markup.Parsers
public class PropertySyntax : ISyntax
{
public string Property { get; set; }
public string Property { get; set; } = string.Empty;
public string Value { get; set; }
public string Value { get; set; } = string.Empty;
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is PropertySyntax &&
((PropertySyntax)obj).Property == Property &&
@ -385,7 +387,7 @@ namespace Avalonia.Markup.Parsers
public class ChildSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is ChildSyntax;
}
@ -393,7 +395,7 @@ namespace Avalonia.Markup.Parsers
public class DescendantSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is DescendantSyntax;
}
@ -401,7 +403,7 @@ namespace Avalonia.Markup.Parsers
public class TemplateSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is TemplateSyntax;
}
@ -409,9 +411,9 @@ namespace Avalonia.Markup.Parsers
public class NotSyntax : ISyntax
{
public IEnumerable<ISyntax> Argument { get; set; }
public IEnumerable<ISyntax> Argument { get; set; } = Enumerable.Empty<ISyntax>();
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return (obj is NotSyntax not) && Argument.SequenceEqual(not.Argument);
}
@ -419,7 +421,7 @@ namespace Avalonia.Markup.Parsers
public class CommaSyntax : ISyntax
{
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return obj is CommaSyntax or;
}

6
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

@ -31,13 +31,13 @@ namespace Avalonia.Markup.Parsers
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The parsed selector.</returns>
public Selector Parse(string s)
public Selector? Parse(string s)
{
var syntax = SelectorGrammar.Parse(s);
return Create(syntax);
}
private Selector Create(IEnumerable<SelectorGrammar.ISyntax> syntax)
private Selector? Create(IEnumerable<SelectorGrammar.ISyntax> syntax)
{
var result = default(Selector);
var results = default(List<Selector>);
@ -110,7 +110,7 @@ namespace Avalonia.Markup.Parsers
results = new List<Selector>();
}
results.Add(result);
results.Add(result ?? throw new NotSupportedException("Invalid selector!"));
result = null;
break;
default:

Loading…
Cancel
Save