Skip to content

Commit 9f2da06

Browse files
committed
Merge branch 'maintenance/code-analysis' into develop
Introduced polyfills for the new .NET 5 `[MemberNotNull()]` and `[MemberNotNullWhen()]` nullability attributes, and applied them to `Topic` in order to get rid of an irritating hack. Migrated the `CompatiblePropertyTopicViewModel` class, from the test project, to use an `init` setter, which not only alleviates a CA2227 warning, but is also more accurate in terms of how the property should be used.
2 parents c15bf2e + 39e3147 commit 9f2da06

4 files changed

Lines changed: 110 additions & 11 deletions

File tree

OnTopic.Tests/ViewModels/CompatiblePropertyTopicViewModel.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@ namespace OnTopic.Tests.ViewModels {
2020
/// <remarks>
2121
/// This is a sample class intended for test purposes only; it is not designed for use in a production environment.
2222
/// </remarks>
23-
[SuppressMessage("Usage", "CA2227", Justification = "This is intended to be initialized by the mapping service.")]
2423
public class CompatiblePropertyTopicViewModel {
2524

2625
public ModelType ModelType { get; set; }
2726

28-
public Collection<DateTime>? VersionHistory { get; set; }
27+
public Collection<DateTime>? VersionHistory { get; init; }
2928

3029
} //Class
3130
} //Namespace
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
namespace System.Diagnostics.CodeAnalysis {
7+
8+
/*============================================================================================================================
9+
| CLASS: MEMBER NOT NULL (ATTRIBUTE)
10+
\---------------------------------------------------------------------------------------------------------------------------*/
11+
/// <summary>
12+
/// Specifies that the method or property will ensure that the listed field and property members have not-null values.
13+
/// </summary>
14+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
15+
internal sealed class MemberNotNullAttribute : Attribute {
16+
17+
/*==========================================================================================================================
18+
| CONSTRUCTOR
19+
\-------------------------------------------------------------------------------------------------------------------------*/
20+
/// <summary>Initializes the attribute with a field or property member.</summary>
21+
/// <param name="member">
22+
/// The field or property member that is promised to be not-null.
23+
/// </param>
24+
#pragma warning disable CA1019 // Define accessors for attribute arguments
25+
public MemberNotNullAttribute(string member) {
26+
Members = new[] { member };
27+
}
28+
#pragma warning restore CA1019 // Define accessors for attribute arguments
29+
30+
/// <summary>Initializes the attribute with the list of field and property members.</summary>
31+
/// <param name="members">
32+
/// The list of field and property members that are promised to be not-null.
33+
/// </param>
34+
public MemberNotNullAttribute(params string[] members) {
35+
Members = members;
36+
}
37+
38+
/*==========================================================================================================================
39+
| PROPERTY: MEMBERS
40+
\-------------------------------------------------------------------------------------------------------------------------*/
41+
/// <summary>Gets field or property member names.</summary>
42+
public string[] Members { get; }
43+
44+
}
45+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
namespace System.Diagnostics.CodeAnalysis {
7+
8+
/*============================================================================================================================
9+
| CLASS: MEMBER NOT NULL (ATTRIBUTE)
10+
\---------------------------------------------------------------------------------------------------------------------------*/
11+
/// <summary>
12+
/// Specifies that the method or property will ensure that the listed field and property members have not-null values when
13+
/// returning with the specified return value condition.
14+
/// </summary>
15+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
16+
internal sealed class MemberNotNullWhenAttribute: Attribute {
17+
18+
/*==========================================================================================================================
19+
| CONSTRUCTOR
20+
\-------------------------------------------------------------------------------------------------------------------------*/
21+
/// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
22+
/// <param name="returnValue">
23+
/// The return value condition. If the method returns this value, the associated parameter will not be null.
24+
/// </param>
25+
/// <param name="member">
26+
/// The field or property member that is promised to be not-null.
27+
/// </param>
28+
#pragma warning disable CA1019 // Define accessors for attribute arguments
29+
public MemberNotNullWhenAttribute(bool returnValue, string member) {
30+
ReturnValue = returnValue;
31+
Members = new[] { member };
32+
}
33+
#pragma warning restore CA1019 // Define accessors for attribute arguments
34+
35+
/// <summary>
36+
/// Initializes the attribute with the specified return value condition and list of field and property members.
37+
/// </summary>
38+
/// <param name="returnValue">
39+
/// The return value condition. If the method returns this value, the associated parameter will not be null.
40+
/// </param>
41+
/// <param name="members">
42+
/// The list of field and property members that are promised to be not-null.
43+
/// </param>
44+
public MemberNotNullWhenAttribute(bool returnValue, params string[] members) {
45+
ReturnValue = returnValue;
46+
Members = members;
47+
}
48+
49+
/*==========================================================================================================================
50+
| PROPERTY: RETURN VALUE
51+
\-------------------------------------------------------------------------------------------------------------------------*/
52+
/// <summary>Gets the return value condition.</summary>
53+
public bool ReturnValue { get; }
54+
55+
/*==========================================================================================================================
56+
| PROPERTY: MEMBERS
57+
\-------------------------------------------------------------------------------------------------------------------------*/
58+
/// <summary>Gets field or property member names.</summary>
59+
public string[] Members { get; }
60+
61+
}
62+
}

OnTopic/Topic.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,6 @@ public Topic(string key, string contentType, Topic? parent = null, int id = -1)
8787
Parent = parent;
8888
}
8989

90-
/*------------------------------------------------------------------------------------------------------------------------
91-
| Initialize key fields
92-
\-----------------------------------------------------------------------------------------------------------------------*/
93-
//###HACK JJC20190924: The local backing fields _key and _contentType are always initialized at this point. But Roslyn's
94-
//flow analysis isn't smart enough to detect this. As such, the following effectively sets _key and _contentType to
95-
//themselves.
96-
_key = Key;
97-
_contentType = ContentType;
98-
9990
}
10091

10192
#region Core Properties
@@ -183,6 +174,7 @@ public Topic? Parent {
183174
/// </value>
184175
public string ContentType {
185176
get => _contentType;
177+
[MemberNotNull(nameof(_contentType))]
186178
set {
187179
TopicFactory.ValidateKey(value);
188180
if (_contentType == value) {
@@ -215,6 +207,7 @@ public string ContentType {
215207
/// </requires>
216208
public string Key {
217209
get => _key;
210+
[MemberNotNull(nameof(_key))]
218211
set {
219212
TopicFactory.ValidateKey(value);
220213
if (_key == value) {

0 commit comments

Comments
 (0)