Skip to content

Commit 8efc0ee

Browse files
committed
Use CallerArgumentExpression for easier calling conventions
1 parent e222260 commit 8efc0ee

File tree

4 files changed

+65
-32
lines changed

4 files changed

+65
-32
lines changed

src/Microsoft.VisualStudio.Validation/Assumes.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics.CodeAnalysis;
77
using System.Globalization;
88
using System.Runtime;
9+
using System.Runtime.CompilerServices;
910

1011
namespace Microsoft;
1112

@@ -128,7 +129,7 @@ public static void Is<T>([NotNull] object? value)
128129
/// </summary>
129130
[DebuggerStepThrough]
130131
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
131-
public static void False([DoesNotReturnIf(true)] bool condition, [Localizable(false)] string? message = null)
132+
public static void False([DoesNotReturnIf(true)] bool condition, [CallerArgumentExpression(nameof(condition)), Localizable(false)] string? message = null)
132133
{
133134
if (condition)
134135
{
@@ -167,7 +168,7 @@ public static void False([DoesNotReturnIf(true)] bool condition, [Localizable(fa
167168
/// </summary>
168169
[DebuggerStepThrough]
169170
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
170-
public static void True([DoesNotReturnIf(false)] bool condition, [Localizable(false)] string? message = null)
171+
public static void True([DoesNotReturnIf(false)] bool condition, [CallerArgumentExpression(nameof(condition)), Localizable(false)] string? message = null)
171172
{
172173
if (!condition)
173174
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
#if !NET5_0_OR_GREATER
5+
6+
#pragma warning disable SA1649 // File name should match first type name
7+
#pragma warning disable SA1600 // Elements should be documented
8+
9+
namespace System.Runtime.CompilerServices
10+
{
11+
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
12+
internal sealed class CallerArgumentExpressionAttribute : Attribute
13+
{
14+
internal CallerArgumentExpressionAttribute(string parameterName)
15+
{
16+
}
17+
}
18+
}
19+
20+
#endif

src/Microsoft.VisualStudio.Validation/Requires.cs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Globalization;
99
using System.Resources;
1010
using System.Runtime;
11+
using System.Runtime.CompilerServices;
1112

1213
namespace Microsoft;
1314

@@ -21,12 +22,12 @@ public static class Requires
2122
/// </summary>
2223
/// <typeparam name="T">The type of the parameter.</typeparam>
2324
/// <param name="value">The value of the argument.</param>
24-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
25+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
2526
/// <returns>The value of the parameter.</returns>
2627
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
2728
[DebuggerStepThrough]
2829
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
29-
public static T NotNull<T>([ValidatedNotNull, NotNull] T value, string? parameterName)
30+
public static T NotNull<T>([ValidatedNotNull, NotNull] T value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
3031
where T : class // ensures value-types aren't passed to a null checking method
3132
{
3233
if (value is null)
@@ -41,12 +42,12 @@ public static T NotNull<T>([ValidatedNotNull, NotNull] T value, string? paramete
4142
/// Throws an exception if the specified parameter's value is IntPtr.Zero.
4243
/// </summary>
4344
/// <param name="value">The value of the argument.</param>
44-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
45+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
4546
/// <returns>The value of the parameter.</returns>
4647
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see cref="IntPtr.Zero"/>.</exception>
4748
[DebuggerStepThrough]
4849
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
49-
public static IntPtr NotNull(IntPtr value, string? parameterName)
50+
public static IntPtr NotNull(IntPtr value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
5051
{
5152
if (value == IntPtr.Zero)
5253
{
@@ -60,15 +61,15 @@ public static IntPtr NotNull(IntPtr value, string? parameterName)
6061
/// Throws an exception if the specified parameter's value is null.
6162
/// </summary>
6263
/// <param name="value">The value of the argument.</param>
63-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
64+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
6465
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
6566
/// <remarks>
6667
/// This method allows async methods to use Requires.NotNull without having to assign the result
6768
/// to local variables to avoid C# warnings.
6869
/// </remarks>
6970
[DebuggerStepThrough]
7071
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
71-
public static void NotNull([ValidatedNotNull, NotNull] Task value, string? parameterName)
72+
public static void NotNull([ValidatedNotNull, NotNull] Task value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
7273
{
7374
if (value is null)
7475
{
@@ -81,15 +82,15 @@ public static void NotNull([ValidatedNotNull, NotNull] Task value, string? param
8182
/// </summary>
8283
/// <typeparam name="T">The type of the return value of the task.</typeparam>
8384
/// <param name="value">The value of the argument.</param>
84-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
85+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
8586
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
8687
/// <remarks>
8788
/// This method allows async methods to use Requires.NotNull without having to assign the result
8889
/// to local variables to avoid C# warnings.
8990
/// </remarks>
9091
[DebuggerStepThrough]
9192
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
92-
public static void NotNull<T>([ValidatedNotNull, NotNull] Task<T> value, string? parameterName)
93+
public static void NotNull<T>([ValidatedNotNull, NotNull] Task<T> value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
9394
{
9495
if (value is null)
9596
{
@@ -102,15 +103,15 @@ public static void NotNull<T>([ValidatedNotNull, NotNull] Task<T> value, string?
102103
/// </summary>
103104
/// <typeparam name="T">The type of the parameter.</typeparam>
104105
/// <param name="value">The value of the argument.</param>
105-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
106+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
106107
/// <returns>The value of the parameter.</returns>
107108
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
108109
/// <remarks>
109110
/// This method exists for callers who themselves only know the type as a generic parameter which
110111
/// may or may not be a class, but certainly cannot be null.
111112
/// </remarks>
112113
[DebuggerStepThrough]
113-
public static T NotNullAllowStructs<T>([ValidatedNotNull, NotNull] T value, string? parameterName)
114+
public static T NotNullAllowStructs<T>([ValidatedNotNull, NotNull] T value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
114115
{
115116
if (value is null)
116117
{
@@ -124,10 +125,10 @@ public static T NotNullAllowStructs<T>([ValidatedNotNull, NotNull] T value, stri
124125
/// Throws an exception if the specified parameter's value is null or empty.
125126
/// </summary>
126127
/// <param name="value">The value of the argument.</param>
127-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
128+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
128129
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="null"/> or empty.</exception>
129130
[DebuggerStepThrough]
130-
public static void NotNullOrEmpty([ValidatedNotNull, NotNull] string value, string? parameterName)
131+
public static void NotNullOrEmpty([ValidatedNotNull, NotNull] string value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
131132
{
132133
// To whoever is doing random code cleaning:
133134
// Consider the performance when changing the code to delegate to NotNull.
@@ -147,10 +148,10 @@ public static void NotNullOrEmpty([ValidatedNotNull, NotNull] string value, stri
147148
/// Throws an exception if the specified parameter's value is null, empty, or whitespace.
148149
/// </summary>
149150
/// <param name="value">The value of the argument.</param>
150-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
151+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
151152
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="null"/> or empty.</exception>
152153
[DebuggerStepThrough]
153-
public static void NotNullOrWhiteSpace([ValidatedNotNull, NotNull] string value, string? parameterName)
154+
public static void NotNullOrWhiteSpace([ValidatedNotNull, NotNull] string value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
154155
{
155156
// To whoever is doing random code cleaning:
156157
// Consider the performance when changing the code to delegate to NotNull.
@@ -176,10 +177,10 @@ public static void NotNullOrWhiteSpace([ValidatedNotNull, NotNull] string value,
176177
/// has no elements.
177178
/// </summary>
178179
/// <param name="values">The value of the argument.</param>
179-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
180+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
180181
/// <exception cref="ArgumentException">Thrown if the tested condition is false.</exception>
181182
[DebuggerStepThrough]
182-
public static void NotNullOrEmpty([ValidatedNotNull, NotNull] IEnumerable values, string? parameterName)
183+
public static void NotNullOrEmpty([ValidatedNotNull, NotNull] IEnumerable values, [CallerArgumentExpression(nameof(values))] string? parameterName = null)
183184
{
184185
// To whoever is doing random code cleaning:
185186
// Consider the performance when changing the code to delegate to NotNull.
@@ -205,10 +206,10 @@ public static void NotNullOrEmpty([ValidatedNotNull, NotNull] IEnumerable values
205206
/// </summary>
206207
/// <typeparam name="T">The type produced by the enumeration.</typeparam>
207208
/// <param name="values">The value of the argument.</param>
208-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
209+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
209210
/// <exception cref="ArgumentException">Thrown if the tested condition is false.</exception>
210211
[DebuggerStepThrough]
211-
public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] IEnumerable<T> values, string? parameterName)
212+
public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] IEnumerable<T> values, [CallerArgumentExpression(nameof(values))] string? parameterName = null)
212213
{
213214
// To whoever is doing random code cleaning:
214215
// Consider the performance when changing the code to delegate to NotNull.
@@ -245,10 +246,10 @@ public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] IEnumerable<T>
245246
/// </summary>
246247
/// <typeparam name="T">The type of value in the collection.</typeparam>
247248
/// <param name="values">The value of the argument.</param>
248-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
249+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
249250
/// <exception cref="ArgumentException">Thrown if the tested condition is false.</exception>
250251
[DebuggerStepThrough]
251-
public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] ICollection<T> values, string? parameterName)
252+
public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] ICollection<T> values, [CallerArgumentExpression(nameof(values))] string? parameterName = null)
252253
{
253254
// To whoever is doing random code cleaning:
254255
// Consider the performance when changing the code to delegate to NotNull.
@@ -270,10 +271,10 @@ public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull] ICollection<T>
270271
/// </summary>
271272
/// <typeparam name="T">The type of the elements in the sequence.</typeparam>
272273
/// <param name="values">The value of the argument.</param>
273-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
274+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
274275
/// <exception cref="ArgumentException">Thrown if the tested condition is false.</exception>
275276
[DebuggerStepThrough]
276-
public static void NotNullEmptyOrNullElements<T>([ValidatedNotNull, NotNull] IEnumerable<T> values, string? parameterName)
277+
public static void NotNullEmptyOrNullElements<T>([ValidatedNotNull, NotNull] IEnumerable<T> values, [CallerArgumentExpression(nameof(values))] string? parameterName = null)
277278
where T : class // ensures value-types aren't passed to a null checking method
278279
{
279280
// To whoever is doing random code cleaning:
@@ -307,10 +308,10 @@ public static void NotNullEmptyOrNullElements<T>([ValidatedNotNull, NotNull] IEn
307308
/// </summary>
308309
/// <typeparam name="T">The type of the elements in the sequence.</typeparam>
309310
/// <param name="values">The value of the argument.</param>
310-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
311+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
311312
/// <exception cref="ArgumentException">Thrown if the tested condition is false.</exception>
312313
[DebuggerStepThrough]
313-
public static void NullOrNotNullElements<T>(IEnumerable<T> values, string? parameterName)
314+
public static void NullOrNotNullElements<T>(IEnumerable<T> values, [CallerArgumentExpression(nameof(values))] string? parameterName = null)
314315
{
315316
if (values is object)
316317
{
@@ -328,10 +329,10 @@ public static void NullOrNotNullElements<T>(IEnumerable<T> values, string? param
328329
/// Throws an exception if the specified parameter's value is <see cref="Guid.Empty"/>.
329330
/// </summary>
330331
/// <param name="value">The value of the argument.</param>
331-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
332+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
332333
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is an empty guid (<see cref="Guid.Empty"/>.)</exception>
333334
[DebuggerStepThrough]
334-
public static void NotEmpty(Guid value, string? parameterName)
335+
public static void NotEmpty(Guid value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
335336
{
336337
if (value == Guid.Empty)
337338
{
@@ -521,9 +522,9 @@ public static Exception Fail(Exception? innerException, string unformattedMessag
521522
/// </summary>
522523
/// <typeparam name="TEnum">The type of enum that may define the <paramref name="value"/>.</typeparam>
523524
/// <param name="value">The value that may be named by <typeparamref name="TEnum"/>.</param>
524-
/// <param name="parameterName">The name of the parameter to include in the exception, if thrown.</param>
525+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
525526
[DebuggerStepThrough]
526-
public static void Defined<TEnum>(TEnum value, string parameterName)
527+
public static void Defined<TEnum>(TEnum value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
527528
where TEnum : struct, Enum
528529
{
529530
if (!Enum.IsDefined(typeof(TEnum), value))
@@ -545,10 +546,10 @@ public static void Defined<TEnum>(TEnum value, string parameterName)
545546
/// </summary>
546547
/// <typeparam name="T">The type of the parameter.</typeparam>
547548
/// <param name="value">The value of the argument.</param>
548-
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
549+
/// <param name="parameterName">The name of the parameter to include in any thrown exception. If this argument is omitted (explicitly writing <see langword="null" /> does not qualify), the expression used in the first argument will be used as the parameter name.</param>
549550
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="null"/> or empty.</exception>
550551
[DebuggerStepThrough]
551-
public static void NotDefault<T>(T value, string parameterName)
552+
public static void NotDefault<T>(T value, [CallerArgumentExpression(nameof(value))] string? parameterName = null)
552553
where T : struct
553554
{
554555
var defaultValue = default(T);

0 commit comments

Comments
 (0)