Skip to content

Commit e8540d8

Browse files
authored
Merge branch 'CommunityToolkit:main' into main
2 parents 3e64e67 + 794b295 commit e8540d8

File tree

110 files changed

+3077
-919
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+3077
-919
lines changed

.gitattributes

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#
1818
# Merging from the command prompt will add diff markers to the files if there
1919
# are conflicts (Merging from VS is not affected by the settings below, in VS
20-
# the diff markers are never inserted). Diff markers may cause the following
20+
# the diff markers are never inserted). Diff markers may cause the following
2121
# file extensions to fail to load in VS. An alternative would be to treat
2222
# these files as binary and thus will always conflict and require user
2323
# intervention with every merge. To do so, just uncomment the entries below
@@ -46,9 +46,9 @@
4646

4747
###############################################################################
4848
# diff behavior for common document formats
49-
#
49+
#
5050
# Convert binary document formats to text before diffing them. This feature
51-
# is only available from the command line. Turn it on by uncommenting the
51+
# is only available from the command line. Turn it on by uncommenting the
5252
# entries below.
5353
###############################################################################
5454
#*.doc diff=astextplain

.runsettings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<RunSettings>
22
<MSTest>
3-
<MaxCpuCount>0</MaxCpuCount>
3+
<MaxCpuCount>0</MaxCpuCount>
44
<Parallelize>
55
<Workers>0</Workers>
66
<Scope>ClassLevel</Scope>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.IO;
6+
using System.Linq;
7+
using System.Reflection;
8+
using System.Text;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.Text;
11+
12+
namespace Microsoft.Toolkit.Mvvm.SourceGenerators
13+
{
14+
/// <summary>
15+
/// A source generator for necessary nullability attributes.
16+
/// </summary>
17+
[Generator]
18+
public sealed class NullabilityAttributesGenerator : ISourceGenerator
19+
{
20+
/// <inheritdoc/>
21+
public void Initialize(GeneratorInitializationContext context)
22+
{
23+
}
24+
25+
/// <inheritdoc/>
26+
public void Execute(GeneratorExecutionContext context)
27+
{
28+
AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullAttribute");
29+
AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute");
30+
}
31+
32+
/// <summary>
33+
/// Adds the source for a given attribute type if it's not present already in the compilation.
34+
/// </summary>
35+
private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName)
36+
{
37+
if (context.Compilation.GetTypeByMetadataName(typeFullName) is not null)
38+
{
39+
return;
40+
}
41+
42+
string
43+
typeName = typeFullName.Split('.').Last(),
44+
filename = $"Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs";
45+
46+
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename);
47+
StreamReader reader = new(stream);
48+
49+
string
50+
originalSource = reader.ReadToEnd(),
51+
outputSource = originalSource.Replace("NETSTANDARD2_0", "true");
52+
53+
context.AddSource($"{typeFullName}.cs", SourceText.From(outputSource, Encoding.UTF8));
54+
}
55+
}
56+
}

Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System;
88
using System.Collections.Generic;
99
using System.ComponentModel;
10+
using System.Diagnostics.CodeAnalysis;
1011
using System.Runtime.CompilerServices;
1112
using System.Threading.Tasks;
1213

@@ -50,7 +51,7 @@ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
5051
/// <remarks>
5152
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
5253
/// </remarks>
53-
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
54+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
5455
{
5556
if (EqualityComparer<T>.Default.Equals(field, newValue))
5657
{
@@ -75,7 +76,7 @@ protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string
7576
/// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
7677
/// <param name="propertyName">(optional) The name of the property that changed.</param>
7778
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
78-
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
79+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
7980
{
8081
if (comparer.Equals(field, newValue))
8182
{
@@ -280,7 +281,7 @@ protected bool SetProperty<TModel, T>(T oldValue, T newValue, IEqualityComparer<
280281
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
281282
/// <see cref="Task"/> instance passed as argument is in any particular state.
282283
/// </remarks>
283-
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
284+
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
284285
{
285286
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
286287
}
@@ -300,7 +301,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
300301
/// <remarks>
301302
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
302303
/// </remarks>
303-
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
304+
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
304305
{
305306
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
306307
}
@@ -338,7 +339,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
338339
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
339340
/// <see cref="Task{TResult}"/> instance passed as argument is in any particular state.
340341
/// </remarks>
341-
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
342+
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
342343
{
343344
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
344345
}
@@ -359,7 +360,7 @@ protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNoti
359360
/// <remarks>
360361
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
361362
/// </remarks>
362-
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
363+
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
363364
{
364365
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
365366
}

Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
<ItemGroup>
1111
<Compile Remove="EmbeddedResources\INotifyPropertyChanged.cs" />
12+
<Compile Remove="EmbeddedResources\NotNullAttribute.cs" />
13+
<Compile Remove="EmbeddedResources\NotNullIfNotNullAttribute.cs" />
1214
<Compile Remove="EmbeddedResources\ObservableObject.cs" />
1315
<Compile Remove="EmbeddedResources\ObservableRecipient.cs" />
1416
</ItemGroup>
@@ -17,6 +19,12 @@
1719
<EmbeddedResource Include="EmbeddedResources\INotifyPropertyChanged.cs">
1820
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1921
</EmbeddedResource>
22+
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\Attributes\NotNullAttribute.cs" Link="EmbeddedResources\NotNullAttribute.cs">
23+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
24+
</EmbeddedResource>
25+
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\Attributes\NotNullIfNotNullAttribute.cs" Link="EmbeddedResources\NotNullIfNotNullAttribute.cs">
26+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
27+
</EmbeddedResource>
2028
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\ComponentModel\ObservableObject.cs" Link="EmbeddedResources\ObservableObject.cs">
2129
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2230
</EmbeddedResource>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if NETSTANDARD2_0
6+
7+
namespace System.Diagnostics.CodeAnalysis
8+
{
9+
/// <summary>
10+
/// Specifies that an output will not be null even if the corresponding type allows it.
11+
/// Specifies that an input argument was not null when the call returns.
12+
/// </summary>
13+
/// <remarks>Internal copy from the BCL attribute.</remarks>
14+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
15+
internal sealed class NotNullAttribute : Attribute
16+
{
17+
}
18+
}
19+
20+
#endif
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if NETSTANDARD2_0
6+
7+
namespace System.Diagnostics.CodeAnalysis
8+
{
9+
/// <summary>
10+
/// Specifies that the output will be non-null if the named parameter is non-null.
11+
/// </summary>
12+
/// <remarks>Internal copy from the BCL attribute.</remarks>
13+
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
14+
internal sealed class NotNullIfNotNullAttribute : Attribute
15+
{
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="NotNullIfNotNullAttribute"/> class.
18+
/// </summary>
19+
/// <param name="parameterName"> The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.</param>
20+
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
21+
22+
/// <summary>Gets the associated parameter name.</summary>
23+
public string ParameterName { get; }
24+
}
25+
}
26+
27+
#endif

Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System;
1919
using System.Collections.Generic;
2020
using System.ComponentModel;
21+
using System.Diagnostics.CodeAnalysis;
2122
using System.Runtime.CompilerServices;
2223
using System.Threading.Tasks;
2324

@@ -84,7 +85,7 @@ protected void OnPropertyChanging([CallerMemberName] string? propertyName = null
8485
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
8586
/// if the current and new value for the target property are the same.
8687
/// </remarks>
87-
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
88+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
8889
{
8990
// We duplicate the code here instead of calling the overload because we can't
9091
// guarantee that the invoked SetProperty<T> will be inlined, and we need the JIT
@@ -120,7 +121,7 @@ protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string
120121
/// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
121122
/// <param name="propertyName">(optional) The name of the property that changed.</param>
122123
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
123-
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
124+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
124125
{
125126
if (comparer.Equals(field, newValue))
126127
{
@@ -340,7 +341,7 @@ protected bool SetProperty<TModel, T>(T oldValue, T newValue, IEqualityComparer<
340341
/// indicates that the new value being assigned to <paramref name="taskNotifier"/> is different than the previous one,
341342
/// and it does not mean the new <see cref="Task"/> instance passed as argument is in any particular state.
342343
/// </remarks>
343-
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
344+
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
344345
{
345346
// We invoke the overload with a callback here to avoid code duplication, and simply pass an empty callback.
346347
// The lambda expression here is transformed by the C# compiler into an empty closure class with a
@@ -368,7 +369,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
368369
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
369370
/// if the current and new value for the target property are the same.
370371
/// </remarks>
371-
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
372+
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
372373
{
373374
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
374375
}
@@ -407,7 +408,7 @@ protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier,
407408
/// indicates that the new value being assigned to <paramref name="taskNotifier"/> is different than the previous one,
408409
/// and it does not mean the new <see cref="Task{TResult}"/> instance passed as argument is in any particular state.
409410
/// </remarks>
410-
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
411+
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
411412
{
412413
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
413414
}
@@ -430,7 +431,7 @@ protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNoti
430431
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
431432
/// if the current and new value for the target property are the same.
432433
/// </remarks>
433-
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
434+
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
434435
{
435436
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
436437
}

Microsoft.Toolkit.Mvvm/ComponentModel/ObservableRecipient.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,9 @@
77
// This file is inspired from the MvvmLight library (lbugnion/MvvmLight),
88
// more info in ThirdPartyNotices.txt in the root of the project.
99

10-
// ================================= NOTE =================================
11-
// This file is mirrored in the ObservableRecipient annotated copy
12-
// (for debugging info) in the Mvvm.SourceGenerators project.
13-
// If any changes are made to this file, they should also be appropriately
14-
// ported to that file as well to keep the behavior consistent.
15-
// ========================================================================
16-
1710
using System;
1811
using System.Collections.Generic;
12+
using System.Diagnostics.CodeAnalysis;
1913
using System.Runtime.CompilerServices;
2014
using Microsoft.Toolkit.Mvvm.Messaging;
2115
using Microsoft.Toolkit.Mvvm.Messaging.Messages;
@@ -145,7 +139,7 @@ protected virtual void Broadcast<T>(T oldValue, T newValue, string? propertyName
145139
/// the <see cref="ObservableObject.PropertyChanging"/> and <see cref="ObservableObject.PropertyChanged"/> events
146140
/// are not raised if the current and new value for the target property are the same.
147141
/// </remarks>
148-
protected bool SetProperty<T>(ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null)
142+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null)
149143
{
150144
T oldValue = field;
151145

@@ -174,7 +168,7 @@ protected bool SetProperty<T>(ref T field, T newValue, bool broadcast, [CallerMe
174168
/// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
175169
/// <param name="propertyName">(optional) The name of the property that changed.</param>
176170
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
177-
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, bool broadcast, [CallerMemberName] string? propertyName = null)
171+
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, bool broadcast, [CallerMemberName] string? propertyName = null)
178172
{
179173
T oldValue = field;
180174

0 commit comments

Comments
 (0)