diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 89e2c096ee..7b9cd0212e 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -2,6 +2,8 @@ netstandard2.0 Avalonia + Enable + CS8600;CS8602;CS8603 diff --git a/src/Markup/Avalonia.Markup/Data/Binding.cs b/src/Markup/Avalonia.Markup/Data/Binding.cs index bf43730481..50be598d7f 100644 --- a/src/Markup/Avalonia.Markup/Data/Binding.cs +++ b/src/Markup/Avalonia.Markup/Data/Binding.cs @@ -39,17 +39,17 @@ namespace Avalonia.Data /// /// Gets or sets the name of the element to use as the binding source. /// - public string ElementName { get; set; } + public string? ElementName { get; set; } /// /// Gets or sets the relative source for the binding. /// - public RelativeSource RelativeSource { get; set; } + public RelativeSource? RelativeSource { get; set; } /// /// Gets or sets the source for the binding. /// - public object Source { get; set; } + public object? Source { get; set; } /// /// Gets or sets the binding path. @@ -59,24 +59,36 @@ namespace Avalonia.Data /// /// Gets or sets a function used to resolve types from names in the binding path. /// - public Func TypeResolver { get; set; } + public Func? 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(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 { diff --git a/src/Markup/Avalonia.Markup/Data/BindingBase.cs b/src/Markup/Avalonia.Markup/Data/BindingBase.cs index 3dbc83a7df..c25ef49167 100644 --- a/src/Markup/Avalonia.Markup/Data/BindingBase.cs +++ b/src/Markup/Avalonia.Markup/Data/BindingBase.cs @@ -38,22 +38,22 @@ namespace Avalonia.Data /// /// Gets or sets the to use. /// - public IValueConverter Converter { get; set; } + public IValueConverter? Converter { get; set; } /// /// Gets or sets a parameter to pass to . /// - public object ConverterParameter { get; set; } + public object? ConverterParameter { get; set; } /// /// Gets or sets the value to use when the binding is unable to produce a value. /// - public object FallbackValue { get; set; } + public object? FallbackValue { get; set; } /// /// Gets or sets the value to use when the binding result is null. /// - public object TargetNullValue { get; set; } + public object? TargetNullValue { get; set; } /// /// Gets or sets the binding mode. @@ -68,26 +68,27 @@ namespace Avalonia.Data /// /// Gets or sets the string format. /// - public string StringFormat { get; set; } + public string? StringFormat { get; set; } - public WeakReference DefaultAnchor { get; set; } + public WeakReference? DefaultAnchor { get; set; } - public WeakReference NameScope { get; set; } + public WeakReference? NameScope { get; set; } protected abstract ExpressionObserver CreateExpressionObserver( IAvaloniaObject target, AvaloniaProperty targetProperty, - object anchor, + object? anchor, bool enableDataValidation); /// public InstancedBinding Initiate( IAvaloniaObject target, AvaloniaProperty targetProperty, - object anchor = null, + object? anchor = null, bool enableDataValidation = false) { - Contract.Requires(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(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(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(target != null); + _ = target ?? throw new ArgumentNullException(nameof(target)); - IObservable controlLocator; + IObservable controlLocator; switch (relativeSource.Tree) { @@ -220,7 +215,7 @@ namespace Avalonia.Data object source, ExpressionNode node) { - Contract.Requires(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(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 GetParentDataContext(IAvaloniaObject target) + protected IObservable 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(); } diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index cbc5f414f2..17b033f238 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -22,12 +22,12 @@ namespace Avalonia.Data /// /// Gets or sets the to use. /// - public IMultiValueConverter Converter { get; set; } + public IMultiValueConverter? Converter { get; set; } /// /// Gets or sets a parameter to pass to . /// - public object ConverterParameter { get; set; } + public object? ConverterParameter { get; set; } /// /// Gets or sets the value to use when the binding is unable to produce a value. @@ -52,12 +52,12 @@ namespace Avalonia.Data /// /// Gets or sets the relative source for the binding. /// - public RelativeSource RelativeSource { get; set; } + public RelativeSource? RelativeSource { get; set; } /// /// Gets or sets the string format. /// - 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 values, Type targetType, IMultiValueConverter converter) + private object ConvertValue(IList 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(values); + values = new System.Collections.ObjectModel.ReadOnlyCollection(values); object converted; if (converter != null) { diff --git a/src/Markup/Avalonia.Markup/Data/RelativeSource.cs b/src/Markup/Avalonia.Markup/Data/RelativeSource.cs index e3d2dd4aaa..037175a137 100644 --- a/src/Markup/Avalonia.Markup/Data/RelativeSource.cs +++ b/src/Markup/Avalonia.Markup/Data/RelativeSource.cs @@ -94,7 +94,7 @@ namespace Avalonia.Data /// /// Gets the type of ancestor to look for when in mode. /// - public Type AncestorType { get; set; } + public Type? AncestorType { get; set; } /// /// Gets or sets a value that describes the type of relative source lookup. diff --git a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs index 83f02c52aa..b6c723d68c 100644 --- a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs +++ b/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 /// /// Gets or sets the to use. /// - public IValueConverter Converter { get; set; } + public IValueConverter? Converter { get; set; } /// /// Gets or sets a parameter to pass to . /// - public object ConverterParameter { get; set; } + public object? ConverterParameter { get; set; } /// /// Gets or sets the binding mode. @@ -83,7 +83,7 @@ namespace Avalonia.Data /// /// Gets or sets the name of the source property on the templated parent. /// - public AvaloniaProperty Property { get; set; } + public AvaloniaProperty? Property { get; set; } /// 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; } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs index 7c362e24cc..439bc15243 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs +++ b/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 Arguments { get; set; } + public IList Arguments { get; set; } = Array.Empty(); } 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; } } } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs index f957bcab1e..ebdad881c3 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs +++ b/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 typeResolver = null, - INameScope nameScope = null) + internal static (ExpressionNode? Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func? 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 typeResolver = null) + string? description = null, + Func? typeResolver = null) { return new ExpressionObserver( root, @@ -45,10 +45,11 @@ namespace Avalonia.Markup.Parsers IObservable rootObservable, string expression, bool enableDataValidation = false, - string description = null, - Func typeResolver = null) + string? description = null, + Func? typeResolver = null) { - Contract.Requires(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 update, bool enableDataValidation = false, - string description = null, - Func typeResolver = null) + string? description = null, + Func? typeResolver = null) { - Contract.Requires(rootGetter != null); + _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter)); return new ExpressionObserver( rootGetter, diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs index 558130e23f..0a6f3f82ba 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs +++ b/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 _typeResolver; - private readonly INameScope _nameScope; + private readonly Func? _typeResolver; + private readonly INameScope? _nameScope; - public ExpressionParser(bool enableValidation, Func typeResolver, INameScope nameScope) + public ExpressionParser(bool enableValidation, Func? 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) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs index 97198145a8..d6068a15d4 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs @@ -9,7 +9,7 @@ namespace Avalonia.Markup.Parsers.Nodes { private readonly WeakReference _nameScope; private readonly string _name; - private IDisposable _subscription; + private IDisposable? _subscription; public ElementNameNode(INameScope nameScope, string name) { diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs index f304d1e9a2..4124032113 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/FindAncestorNode.cs +++ b/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; diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs index f3abd6a5c5..2f1756fc55 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs +++ b/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 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; diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs index c5953b514c..250eca1852 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/PropertyPathGrammar.cs +++ b/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(); 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; diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index b25e9490cd..9d03341f92 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/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(); 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 Argument { get; set; } + public IEnumerable Argument { get; set; } = Enumerable.Empty(); - 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; } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs index 04519bf2bb..92ba744ee1 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs @@ -31,13 +31,13 @@ namespace Avalonia.Markup.Parsers /// /// The string. /// The parsed selector. - public Selector Parse(string s) + public Selector? Parse(string s) { var syntax = SelectorGrammar.Parse(s); return Create(syntax); } - private Selector Create(IEnumerable syntax) + private Selector? Create(IEnumerable syntax) { var result = default(Selector); var results = default(List); @@ -110,7 +110,7 @@ namespace Avalonia.Markup.Parsers results = new List(); } - results.Add(result); + results.Add(result ?? throw new NotSupportedException("Invalid selector!")); result = null; break; default: