diff --git a/samples/ControlCatalog/Pages/SliderPage.xaml b/samples/ControlCatalog/Pages/SliderPage.xaml
index c6f5521e60..ea31ed0050 100644
--- a/samples/ControlCatalog/Pages/SliderPage.xaml
+++ b/samples/ControlCatalog/Pages/SliderPage.xaml
@@ -6,11 +6,22 @@
A control that lets the user select from a range of values by moving a Thumb control along a Track.
-
+
+
+
+
internal int Id { get; }
- internal bool HasChangedSubscriptions => _changed?.HasObservers ?? false;
-
///
/// Provides access to a property's binding via the
/// indexer.
@@ -512,7 +510,7 @@ namespace Avalonia
///
/// An if setting the property can be undone, otherwise null.
///
- internal abstract IDisposable? RouteSetValue(
+ internal abstract IDisposable RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority);
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index 4a3b104f2a..4cde965400 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -362,7 +362,7 @@ namespace Avalonia
/// The property.
///
/// You won't usually want to call this method directly, instead use the
- ///
+ ///
/// method.
///
public void Register(Type type, AvaloniaProperty property)
@@ -413,7 +413,7 @@ namespace Avalonia
/// The property.
///
/// You won't usually want to call this method directly, instead use the
- ///
+ ///
/// method.
///
public void RegisterAttached(Type type, AvaloniaProperty property)
diff --git a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
index 9bc3609dc5..7a233a62ab 100644
--- a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
+++ b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
@@ -13,9 +13,11 @@ namespace Avalonia.Collections.Pooled
public interface IReadOnlyPooledList : IReadOnlyList
{
+#pragma warning disable CS0419
///
/// Gets a for the items currently in the collection.
///
+#pragma warning restore CS0419
ReadOnlySpan Span { get; }
}
}
diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs
index f0d6b292cc..e50e100d32 100644
--- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs
+++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs
@@ -138,7 +138,6 @@ namespace Avalonia.Collections.Pooled
/// initially empty, but will have room for the given number of elements
/// before any reallocations are required.
///
- /// If true, Count of list equals capacity. Depending on ClearMode, rented items may or may not hold dirty values.
public PooledList(int capacity, ClearMode clearMode, ArrayPool customPool, bool sizeToCapacity)
{
if (capacity < 0)
@@ -499,11 +498,13 @@ namespace Avalonia.Collections.Pooled
public void AddRange(T[] array)
=> AddRange(array.AsSpan());
+#pragma warning disable CS0419
///
/// Adds the elements of the given to the end of this list. If
/// required, the capacity of the list is increased to twice the previous
/// capacity or the new size, whichever is larger.
///
+#pragma warning restore CS0419
public void AddRange(ReadOnlySpan span)
{
var newSpan = InsertSpan(_size, span.Length, false);
diff --git a/src/Avalonia.Base/Data/BindingValue.cs b/src/Avalonia.Base/Data/BindingValue.cs
index 9aac1bacba..6e3c9ae67b 100644
--- a/src/Avalonia.Base/Data/BindingValue.cs
+++ b/src/Avalonia.Base/Data/BindingValue.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using Avalonia.Utilities;
#nullable enable
@@ -81,14 +82,14 @@ namespace Avalonia.Data
///
public readonly struct BindingValue
{
- private readonly T _value;
+ [AllowNull] private readonly T _value;
///
/// Initializes a new instance of the struct with a type of
///
///
/// The value.
- public BindingValue(T value)
+ public BindingValue([AllowNull] T value)
{
ValidateValue(value);
_value = value;
@@ -96,7 +97,7 @@ namespace Avalonia.Data
Error = null;
}
- private BindingValue(BindingValueType type, T value, Exception? error)
+ private BindingValue(BindingValueType type, [AllowNull] T value, Exception? error)
{
_value = value;
Type = type;
@@ -154,7 +155,7 @@ namespace Avalonia.Data
BindingValueType.UnsetValue => AvaloniaProperty.UnsetValue,
BindingValueType.DoNothing => BindingOperations.DoNothing,
BindingValueType.Value => _value,
- BindingValueType.BindingError =>
+ BindingValueType.BindingError =>
new BindingNotification(Error, BindingErrorType.Error),
BindingValueType.BindingErrorWithFallback =>
new BindingNotification(Error, BindingErrorType.Error, Value),
@@ -175,7 +176,7 @@ namespace Avalonia.Data
/// The binding type is or
/// .
///
- public BindingValue WithValue(T value)
+ public BindingValue WithValue([AllowNull] T value)
{
if (Type == BindingValueType.DoNothing)
{
@@ -190,6 +191,7 @@ namespace Avalonia.Data
/// Gets the value of the binding value if present, otherwise the default value.
///
/// The value.
+ [return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
///
@@ -206,6 +208,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
///
+ [return: MaybeNull]
public TResult GetValueOrDefault()
{
return HasValue ?
@@ -222,7 +225,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or if the
/// value is not present.
///
- public TResult GetValueOrDefault(TResult defaultValue)
+ [return: MaybeNull]
+ public TResult GetValueOrDefault([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@@ -242,7 +246,7 @@ namespace Avalonia.Data
UnsetValueType _ => Unset,
DoNothingType _ => DoNothing,
BindingNotification n => n.ToBindingValue().Cast(),
- _ => (T)value
+ _ => new BindingValue((T)value)
};
}
@@ -250,7 +254,7 @@ namespace Avalonia.Data
/// Creates a binding value from an instance of the underlying value type.
///
/// The value.
- public static implicit operator BindingValue(T value) => new BindingValue(value);
+ public static implicit operator BindingValue([AllowNull] T value) => new BindingValue(value);
///
/// Creates a binding value from an .
@@ -278,7 +282,7 @@ namespace Avalonia.Data
/// The binding error.
public static BindingValue BindingError(Exception e)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(BindingValueType.BindingError, default, e);
}
@@ -290,7 +294,7 @@ namespace Avalonia.Data
/// The fallback value.
public static BindingValue BindingError(Exception e, T fallbackValue)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(BindingValueType.BindingErrorWithFallback, fallbackValue, e);
}
@@ -303,7 +307,7 @@ namespace Avalonia.Data
/// The fallback value.
public static BindingValue BindingError(Exception e, Optional fallbackValue)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(
fallbackValue.HasValue ?
@@ -319,7 +323,7 @@ namespace Avalonia.Data
/// The data validation error.
public static BindingValue DataValidationError(Exception e)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(BindingValueType.DataValidationError, default, e);
}
@@ -331,7 +335,7 @@ namespace Avalonia.Data
/// The fallback value.
public static BindingValue DataValidationError(Exception e, T fallbackValue)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(BindingValueType.DataValidationErrorWithFallback, fallbackValue, e);
}
@@ -344,7 +348,7 @@ namespace Avalonia.Data
/// The fallback value.
public static BindingValue DataValidationError(Exception e, Optional fallbackValue)
{
- e = e ?? throw new ArgumentNullException("e");
+ e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue(
fallbackValue.HasValue ?
@@ -354,7 +358,7 @@ namespace Avalonia.Data
e);
}
- private static void ValidateValue(T value)
+ private static void ValidateValue([AllowNull] T value)
{
if (value is UnsetValueType)
{
diff --git a/src/Avalonia.Base/Data/Optional.cs b/src/Avalonia.Base/Data/Optional.cs
index dd952c895c..8e044d7896 100644
--- a/src/Avalonia.Base/Data/Optional.cs
+++ b/src/Avalonia.Base/Data/Optional.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
#nullable enable
@@ -22,13 +23,13 @@ namespace Avalonia.Data
///
public readonly struct Optional : IEquatable>
{
- private readonly T _value;
+ [AllowNull] private readonly T _value;
///
/// Initializes a new instance of the struct with value.
///
/// The value.
- public Optional(T value)
+ public Optional([AllowNull] T value)
{
_value = value;
HasValue = true;
@@ -48,7 +49,7 @@ namespace Avalonia.Data
public T Value => HasValue ? _value : throw new InvalidOperationException("Optional has no value.");
///
- public override bool Equals(object obj) => obj is Optional o && this == o;
+ public override bool Equals(object? obj) => obj is Optional o && this == o;
///
public bool Equals(Optional other) => this == other;
@@ -69,6 +70,7 @@ namespace Avalonia.Data
/// Gets the value if present, otherwise the default value.
///
/// The value.
+ [return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
///
@@ -85,6 +87,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
///
+ [return: MaybeNull]
public TResult GetValueOrDefault()
{
return HasValue ?
@@ -101,7 +104,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or if the
/// value is not present.
///
- public TResult GetValueOrDefault(TResult defaultValue)
+ [return: MaybeNull]
+ public TResult GetValueOrDefault([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@@ -112,7 +116,7 @@ namespace Avalonia.Data
/// Creates an from an instance of the underlying value type.
///
/// The value.
- public static implicit operator Optional(T value) => new Optional(value);
+ public static implicit operator Optional([AllowNull] T value) => new Optional(value);
///
/// Compares two s for inequality.
@@ -128,7 +132,7 @@ namespace Avalonia.Data
/// The first value.
/// The second value.
/// True if the values are equal; otherwise false.
- public static bool operator==(Optional x, Optional y)
+ public static bool operator ==(Optional x, Optional y)
{
if (!x.HasValue && !y.HasValue)
{
diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs
index 0e65379abd..d42c030245 100644
--- a/src/Avalonia.Base/DirectPropertyBase.cs
+++ b/src/Avalonia.Base/DirectPropertyBase.cs
@@ -120,7 +120,7 @@ namespace Avalonia
return o.GetValue(this);
}
- internal override object RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
+ internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
{
return o.GetValue(this);
}
diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs
index f567cd930c..a68b65e5e0 100644
--- a/src/Avalonia.Base/IStyledPropertyMetadata.cs
+++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs
@@ -1,5 +1,3 @@
-using System;
-
namespace Avalonia
{
///
diff --git a/src/Avalonia.Base/Metadata/NullableAttributes.cs b/src/Avalonia.Base/Metadata/NullableAttributes.cs
new file mode 100644
index 0000000000..91f5e81863
--- /dev/null
+++ b/src/Avalonia.Base/Metadata/NullableAttributes.cs
@@ -0,0 +1,140 @@
+#pragma warning disable MA0048 // File name must match type name
+#define INTERNAL_NULLABLE_ATTRIBUTES
+#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48
+
+// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ /// Specifies that null is allowed as an input even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class AllowNullAttribute : Attribute
+ { }
+
+ /// Specifies that null is disallowed as an input even if the corresponding type allows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class DisallowNullAttribute : Attribute
+ { }
+
+ /// Specifies that an output may be null even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class MaybeNullAttribute : Attribute
+ { }
+
+ /// Specifies that an output will not be null even if the corresponding type allows it.
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class NotNullAttribute : Attribute
+ { }
+
+ /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class MaybeNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter may be null.
+ ///
+ public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+ }
+
+ /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class NotNullWhenAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified return value condition.
+ ///
+ /// The return value condition. If the method returns this value, the associated parameter will not be null.
+ ///
+ public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ /// Gets the return value condition.
+ public bool ReturnValue { get; }
+ }
+
+ /// Specifies that the output will be non-null if the named parameter is non-null.
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class NotNullIfNotNullAttribute : Attribute
+ {
+ /// Initializes the attribute with the associated parameter name.
+ ///
+ /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
+ ///
+ public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
+
+ /// Gets the associated parameter name.
+ public string ParameterName { get; }
+ }
+
+ /// Applied to a method that will never return under any circumstance.
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class DoesNotReturnAttribute : Attribute
+ { }
+
+ /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value.
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+#if INTERNAL_NULLABLE_ATTRIBUTES
+ internal
+#else
+ public
+#endif
+ sealed class DoesNotReturnIfAttribute : Attribute
+ {
+ /// Initializes the attribute with the specified parameter value.
+ ///
+ /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
+ /// the associated parameter matches this value.
+ ///
+ public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
+
+ /// Gets the condition parameter value.
+ public bool ParameterValue { get; }
+ }
+}
+#endif
diff --git a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs b/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs
index 6ed6c2ef52..4d82381323 100644
--- a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs
@@ -1,7 +1,4 @@
-using System;
-using Avalonia.Data;
-
-#nullable enable
+#nullable enable
namespace Avalonia.PropertyStore
{
@@ -10,8 +7,6 @@ namespace Avalonia.PropertyStore
///
internal interface IPriorityValueEntry : IValue
{
- BindingPriority Priority { get; }
-
void Reparent(IValueSink sink);
}
diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
index 59c017bc09..859e9ba81c 100644
--- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
@@ -1,4 +1,5 @@
-using Avalonia.Data;
+using System.Diagnostics.CodeAnalysis;
+using Avalonia.Data;
#nullable enable
@@ -11,9 +12,9 @@ namespace Avalonia.PropertyStore
/// The property type.
internal class LocalValueEntry : IValue
{
- private T _value;
+ [AllowNull] private T _value;
- public LocalValueEntry(T value) => _value = value;
+ public LocalValueEntry([AllowNull] T value) => _value = value;
public BindingPriority Priority => BindingPriority.LocalValue;
Optional