diff --git a/MimeKit/Header.cs b/MimeKit/Header.cs index 6a23a9b184..8d28196268 100644 --- a/MimeKit/Header.cs +++ b/MimeKit/Header.cs @@ -24,9 +24,12 @@ // THE SOFTWARE. // +#nullable enable + using System; using System.Text; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using MimeKit.Utils; @@ -53,7 +56,7 @@ public class Header readonly byte[] rawField; bool explicitRawValue; - string textValue; + string? textValue; byte[] rawValue; /// @@ -678,7 +681,7 @@ static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, int count = 0; while (index < rawValue.Length) { - ReceivedTokenValue token = null; + ReceivedTokenValue? token = null; int startIndex = index; if (!ParseUtils.SkipCommentsAndWhiteSpace (rawValue, ref index, rawValue.Length, false) || index >= rawValue.Length) { @@ -1443,6 +1446,7 @@ internal byte[] GetRawValue (FormatOptions format) /// -or- /// is . /// + [MemberNotNull (nameof (rawValue))] public void SetValue (FormatOptions format, Encoding encoding, string value) { if (format is null) @@ -1483,6 +1487,7 @@ public void SetValue (FormatOptions format, Encoding encoding, string value) /// -or- /// is . /// + [MemberNotNull (nameof (rawValue))] public void SetValue (Encoding encoding, string value) { SetValue (FormatOptions.Default, encoding, value); @@ -1575,7 +1580,7 @@ public void SetRawValue (byte[] value) OnChanged (); } - internal event EventHandler Changed; + internal event EventHandler? Changed; void OnChanged () { @@ -1655,7 +1660,7 @@ static bool IsBlank (byte c) return c.IsBlank (); } - internal static unsafe bool TryParse (ParserOptions options, byte* input, int length, bool strict, out Header header) + internal static unsafe bool TryParse (ParserOptions options, byte* input, int length, bool strict, [NotNullWhen (true)] out Header? header) { byte* inend = input + length; byte* start = input; @@ -1749,7 +1754,7 @@ internal static unsafe bool TryParse (ParserOptions options, byte* input, int le /// and do not specify /// a valid range in the byte array. /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out Header header) + public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, [NotNullWhen (true)] out Header? header) { ParseUtils.ValidateArguments (options, buffer, startIndex, length); @@ -1779,7 +1784,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde /// and do not specify /// a valid range in the byte array. /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out Header header) + public static bool TryParse (byte[] buffer, int startIndex, int length, [NotNullWhen (true)] out Header? header) { return TryParse (ParserOptions.Default, buffer, startIndex, length, out header); } @@ -1803,7 +1808,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Head /// /// is out of range. /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out Header header) + public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, [NotNullWhen (true)] out Header? header) { ParseUtils.ValidateArguments (options, buffer, startIndex); @@ -1832,7 +1837,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde /// /// is out of range. /// - public static bool TryParse (byte[] buffer, int startIndex, out Header header) + public static bool TryParse (byte[] buffer, int startIndex, [NotNullWhen (true)] out Header? header) { return TryParse (ParserOptions.Default, buffer, startIndex, out header); } @@ -1852,7 +1857,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out Header header) /// -or- /// is . /// - public static bool TryParse (ParserOptions options, byte[] buffer, out Header header) + public static bool TryParse (ParserOptions options, byte[] buffer, [NotNullWhen (true)] out Header? header) { return TryParse (options, buffer, 0, out header); } @@ -1869,7 +1874,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out Header he /// /// is . /// - public static bool TryParse (byte[] buffer, out Header header) + public static bool TryParse (byte[] buffer, [NotNullWhen (true)] out Header? header) { return TryParse (ParserOptions.Default, buffer, out header); } @@ -1889,7 +1894,7 @@ public static bool TryParse (byte[] buffer, out Header header) /// -or- /// is . /// - public static bool TryParse (ParserOptions options, string text, out Header header) + public static bool TryParse (ParserOptions options, string text, [NotNullWhen (true)] out Header? header) { ParseUtils.ValidateArguments (options, text); @@ -1914,7 +1919,7 @@ public static bool TryParse (ParserOptions options, string text, out Header head /// /// is . /// - public static bool TryParse (string text, out Header header) + public static bool TryParse (string text, [NotNullWhen (true)] out Header? header) { return TryParse (ParserOptions.Default, text, out header); } diff --git a/MimeKit/HeaderId.cs b/MimeKit/HeaderId.cs index 6fc40f74ff..33059dc97c 100644 --- a/MimeKit/HeaderId.cs +++ b/MimeKit/HeaderId.cs @@ -24,6 +24,8 @@ // THE SOFTWARE. // +#nullable enable + using System; using System.Collections.Generic; diff --git a/MimeKit/HeaderList.cs b/MimeKit/HeaderList.cs index 0a831e062a..30a27b519d 100644 --- a/MimeKit/HeaderList.cs +++ b/MimeKit/HeaderList.cs @@ -24,6 +24,8 @@ // THE SOFTWARE. // +#nullable enable + using System; using System.IO; using System.Text; @@ -31,6 +33,7 @@ using System.Collections; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit.IO; using MimeKit.Utils; @@ -594,7 +597,7 @@ public void Replace (string field, string value) /// /// is . /// - public string this [HeaderId id] { + public string? this [HeaderId id] { get { if (id == HeaderId.Unknown) throw new ArgumentOutOfRangeException (nameof (id)); @@ -634,7 +637,7 @@ public string this [HeaderId id] { /// -or- /// is . /// - public string this [string field] { + public string? this [string field] { get { if (field is null) throw new ArgumentNullException (nameof (field)); @@ -1205,24 +1208,24 @@ IEnumerator IEnumerable.GetEnumerator () #endregion - internal event EventHandler Changed; + internal event EventHandler? Changed; - void HeaderChanged (object sender, EventArgs args) + void HeaderChanged (object? sender, EventArgs args) { - OnChanged ((Header) sender, HeaderListChangedAction.Changed); + OnChanged (sender as Header, HeaderListChangedAction.Changed); } - void OnChanged (Header header, HeaderListChangedAction action) + void OnChanged (Header? header, HeaderListChangedAction action) { Changed?.Invoke (this, new HeaderListChangedEventArgs (header, action)); } - internal bool TryGetHeader (HeaderId id, out Header header) + internal bool TryGetHeader (HeaderId id, [NotNullWhen (true)] out Header? header) { return table.TryGetValue (id.ToHeaderName (), out header); } - internal bool TryGetHeader (string field, out Header header) + internal bool TryGetHeader (string field, [NotNullWhen (true)] out Header? header) { return table.TryGetValue (field, out header); } diff --git a/MimeKit/HeaderListChangedEventArgs.cs b/MimeKit/HeaderListChangedEventArgs.cs index a2781afa09..159cc62b76 100644 --- a/MimeKit/HeaderListChangedEventArgs.cs +++ b/MimeKit/HeaderListChangedEventArgs.cs @@ -24,6 +24,8 @@ // THE SOFTWARE. // +#nullable enable + using System; namespace MimeKit { @@ -57,7 +59,7 @@ public enum HeaderListChangedAction { class HeaderListChangedEventArgs : EventArgs { - internal HeaderListChangedEventArgs (Header header, HeaderListChangedAction action) + internal HeaderListChangedEventArgs (Header? header, HeaderListChangedAction action) { Header = header; Action = action; @@ -67,7 +69,7 @@ public HeaderListChangedAction Action { get; private set; } - public Header Header { + public Header? Header { get; private set; } } diff --git a/MimeKit/HeaderListCollection.cs b/MimeKit/HeaderListCollection.cs index 420cb4d112..ee48b72cdc 100644 --- a/MimeKit/HeaderListCollection.cs +++ b/MimeKit/HeaderListCollection.cs @@ -24,6 +24,8 @@ // THE SOFTWARE. // +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -236,14 +238,14 @@ IEnumerator IEnumerable.GetEnumerator () return GetEnumerator (); } - internal event EventHandler Changed; + internal event EventHandler? Changed; void OnChanged () { Changed?.Invoke (this, EventArgs.Empty); } - void OnGroupChanged (object sender, HeaderListChangedEventArgs e) + void OnGroupChanged (object? sender, HeaderListChangedEventArgs e) { OnChanged (); } diff --git a/MimeKit/NullableAttributes.cs b/MimeKit/NullableAttributes.cs new file mode 100644 index 0000000000..9c5771483d --- /dev/null +++ b/MimeKit/NullableAttributes.cs @@ -0,0 +1,123 @@ +// +// NullableAttributes.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2024 .NET Foundation and Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +namespace System.Diagnostics.CodeAnalysis { +#if NETFRAMEWORK || NETSTANDARD2_0 + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + sealed class AllowNullAttribute : Attribute + { + public AllowNullAttribute () { } + } + + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + sealed class DisallowNullAttribute : Attribute + { + public DisallowNullAttribute () { } + } + + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] + sealed class DoesNotReturnAttribute : Attribute + { + public DoesNotReturnAttribute () { } + } + + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] + sealed class DoesNotReturnIfAttribute : Attribute + { + public DoesNotReturnIfAttribute (bool parameterValue) => ParameterValue = parameterValue; + + public bool ParameterValue { get; } + } + + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + sealed class MaybeNullAttribute : Attribute + { + public MaybeNullAttribute () { } + } + + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] + sealed class MaybeNullWhenAttribute : Attribute + { + public MaybeNullWhenAttribute (bool returnValue) => ReturnValue = returnValue; + + public bool ReturnValue { get; } + } + + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + sealed class NotNullAttribute : Attribute + { + public NotNullAttribute () { } + } + + [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + sealed class NotNullIfNotNullAttribute : Attribute + { + public NotNullIfNotNullAttribute (string parameterName) => ParameterName = parameterName; + + public string ParameterName { get; } + } + + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] + sealed class NotNullWhenAttribute : Attribute + { + public NotNullWhenAttribute (bool returnValue) => ReturnValue = returnValue; + + public bool ReturnValue { get; } + } +#endif + +#if NETFRAMEWORK || NETSTANDARD2_0 || NETSTANDARD2_1 + [AttributeUsage (AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + public sealed class MemberNotNullAttribute : Attribute + { + public MemberNotNullAttribute (string member) => Members = new string[] { member }; + + public MemberNotNullAttribute (params string[] members) => Members = members; + + public string[] Members { get; } + } + + [AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + sealed class MemberNotNullWhenAttribute : Attribute + { + public MemberNotNullWhenAttribute (bool returnValue, string member) + { + Members = new string[] { member }; + ReturnValue = returnValue; + } + + public MemberNotNullWhenAttribute (bool returnValue, string[] members) + { + Members = members; + ReturnValue = returnValue; + } + + public string[] Members { get; } + + public bool ReturnValue { get; } + } +#endif +}