Skip to content

Commit 9f2d3de

Browse files
authored
Merge pull request #83 from cdonnellytx/feature/noEnumeration
Mark methods with NoEnumerationAttribute.
2 parents a66ecf8 + f3696b4 commit 9f2d3de

3 files changed

Lines changed: 55 additions & 24 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ ModelManifest.xml
218218
# TargetFrameworks.props files
219219
**/TargetFrameworks.props
220220

221+
# JetBrains Rider
222+
*.sln.iml
223+
.idea/
224+
221225
# The source code redistributable of Light.GuardClauses
222226
/Code/Light.GuardClauses.Source/Light.GuardClauses.cs
223227
/Code/Light.GuardClauses.SourceCodeTransformation/settings.json

Code/Light.GuardClauses/Check.CommonAssertions.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static partial class Check
2020
/// <exception cref="ArgumentNullException">Thrown when <paramref name="parameter" /> is null.</exception>
2121
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2222
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")]
23-
public static T MustNotBeNull<T>([ValidatedNotNull] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class
23+
public static T MustNotBeNull<T>([ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class
2424
{
2525
if (parameter is null)
2626
Throw.ArgumentNull(parameterName, message);
@@ -35,7 +35,7 @@ public static T MustNotBeNull<T>([ValidatedNotNull] this T? parameter, [CallerAr
3535
/// <exception cref="Exception">Your custom exception thrown when <paramref name="parameter" /> is null.</exception>
3636
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3737
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")]
38-
public static T MustNotBeNull<T>([ValidatedNotNull] this T? parameter, Func<Exception> exceptionFactory) where T : class
38+
public static T MustNotBeNull<T>([ValidatedNotNull, NoEnumeration] this T? parameter, Func<Exception> exceptionFactory) where T : class
3939
{
4040
if (parameter is null)
4141
Throw.CustomException(exceptionFactory);
@@ -101,7 +101,7 @@ public static T MustNotBeDefault<T>([ValidatedNotNull] this T parameter, Func<Ex
101101
/// <exception cref="ArgumentNullException">Thrown when <typeparamref name="T" /> is a reference type and <paramref name="parameter" /> is null.</exception>
102102
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103103
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")]
104-
public static T MustNotBeNullReference<T>([ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null)
104+
public static T MustNotBeNullReference<T>([ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null)
105105
{
106106
if (default(T) != null)
107107
return parameter;
@@ -121,7 +121,7 @@ public static T MustNotBeNullReference<T>([ValidatedNotNull] this T parameter, [
121121
/// <exception cref="Exception">Your custom exception thrown when <typeparamref name="T" /> is a reference type and <paramref name="parameter" /> is null.</exception>
122122
[MethodImpl(MethodImplOptions.AggressiveInlining)]
123123
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")]
124-
public static T MustNotBeNullReference<T>([ValidatedNotNull] this T parameter, Func<Exception> exceptionFactory)
124+
public static T MustNotBeNullReference<T>([ValidatedNotNull, NoEnumeration] this T parameter, Func<Exception> exceptionFactory)
125125
{
126126
if (default(T) != null)
127127
return parameter;
@@ -141,7 +141,7 @@ public static T MustNotBeNullReference<T>([ValidatedNotNull] this T parameter, F
141141
/// <exception cref="ArgumentNullException">Thrown when <paramref name="parameter" /> is null.</exception>
142142
[MethodImpl(MethodImplOptions.AggressiveInlining)]
143143
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")]
144-
public static T MustBeOfType<T>([ValidatedNotNull] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null)
144+
public static T MustBeOfType<T>([ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null)
145145
{
146146
if (parameter.MustNotBeNull(parameterName, message) is T castValue)
147147
return castValue;
@@ -158,7 +158,7 @@ public static T MustBeOfType<T>([ValidatedNotNull] this object? parameter, [Call
158158
/// <exception cref="Exception">Your custom exception thrown when <paramref name="parameter" /> cannot be cast to <typeparamref name="T" />.</exception>
159159
[MethodImpl(MethodImplOptions.AggressiveInlining)]
160160
[ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")]
161-
public static T MustBeOfType<T>([ValidatedNotNull] this object? parameter, Func<object?, Exception> exceptionFactory)
161+
public static T MustBeOfType<T>([ValidatedNotNull, NoEnumeration] this object? parameter, Func<object?, Exception> exceptionFactory)
162162
{
163163
if (parameter is T castValue)
164164
return castValue;
@@ -328,7 +328,7 @@ public static void InvalidArgument<T>(bool condition, T parameter, Func<T, Excep
328328
/// <param name="message">The message that will be passed to the resulting exception (optional).</param>
329329
/// <exception cref="NullableHasNoValueException">Thrown when <paramref name="parameter" /> has no value.</exception>
330330
[MethodImpl(MethodImplOptions.AggressiveInlining)]
331-
public static T MustHaveValue<T>(this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct
331+
public static T MustHaveValue<T>([NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct
332332
{
333333
if (!parameter.HasValue)
334334
Throw.NullableHasNoValue(parameterName, message);
@@ -344,7 +344,7 @@ public static T MustHaveValue<T>(this T? parameter, [CallerArgumentExpression("p
344344
/// <exception cref="NullableHasNoValueException">Thrown when <paramref name="parameter" /> has no value.</exception>
345345
[MethodImpl(MethodImplOptions.AggressiveInlining)]
346346
[ContractAnnotation("exceptionFactory:null => halt")]
347-
public static T MustHaveValue<T>(this T? parameter, Func<Exception> exceptionFactory) where T : struct
347+
public static T MustHaveValue<T>([NoEnumeration] this T? parameter, Func<Exception> exceptionFactory) where T : struct
348348
{
349349
if (!parameter.HasValue)
350350
Throw.CustomException(exceptionFactory);
@@ -359,7 +359,7 @@ public static T MustHaveValue<T>(this T? parameter, Func<Exception> exceptionFac
359359
// ReSharper disable StringLiteralTypo
360360
[ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")]
361361
// ReSharper restore StringLiteralTypo
362-
public static bool IsSameAs<T>(this T? parameter, T? other) where T : class =>
362+
public static bool IsSameAs<T>([NoEnumeration] this T? parameter, [NoEnumeration] T? other) where T : class =>
363363
ReferenceEquals(parameter, other);
364364

365365
/// <summary>
@@ -372,7 +372,7 @@ public static bool IsSameAs<T>(this T? parameter, T? other) where T : class =>
372372
/// <param name="message">The message that will be passed to the resulting exception (optional).</param>
373373
/// <exception cref="SameObjectReferenceException">Thrown when both <paramref name="parameter" /> and <paramref name="other" /> point to the same object.</exception>
374374
[MethodImpl(MethodImplOptions.AggressiveInlining)]
375-
public static T? MustNotBeSameAs<T>(this T? parameter, T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class
375+
public static T? MustNotBeSameAs<T>([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class
376376
{
377377
if (ReferenceEquals(parameter, other))
378378
Throw.SameObjectReference(parameter, parameterName, message);
@@ -388,7 +388,7 @@ public static bool IsSameAs<T>(this T? parameter, T? other) where T : class =>
388388
/// <param name="exceptionFactory">The delegate that creates your custom exception. <paramref name="parameter" /> is passed to this delegate.</param>
389389
/// <exception cref="SameObjectReferenceException">Thrown when both <paramref name="parameter" /> and <paramref name="other" /> point to the same object.</exception>
390390
[MethodImpl(MethodImplOptions.AggressiveInlining)]
391-
public static T? MustNotBeSameAs<T>(this T? parameter, T? other, Func<T?, Exception> exceptionFactory) where T : class
391+
public static T? MustNotBeSameAs<T>([NoEnumeration] this T? parameter, T? other, Func<T?, Exception> exceptionFactory) where T : class
392392
{
393393
if (ReferenceEquals(parameter, other))
394394
Throw.CustomException(exceptionFactory, parameter);

Code/Light.GuardClauses/ReSharperAnnotations.cs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,21 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
SOFTWARE. */
2222

23+
#nullable disable
24+
2325
using System;
2426
// ReSharper disable CheckNamespace
27+
// ReSharper disable UnusedMember.Global
28+
// ReSharper disable MemberCanBePrivate.Global
29+
// ReSharper disable UnusedAutoPropertyAccessor.Global
30+
// ReSharper disable IntroduceOptionalParameters.Global
31+
// ReSharper disable MemberCanBeProtected.Global
32+
// ReSharper disable InconsistentNaming
2533

2634
namespace JetBrains.Annotations;
2735

2836
/// <summary>
29-
/// Indicates that the value of the marked element could never be <c>null</c>.
37+
/// Indicates that the value of the marked element can never be <c>null</c>.
3038
/// </summary>
3139
/// <example><code>
3240
/// [NotNull] object Foo() {
@@ -40,7 +48,7 @@ namespace JetBrains.Annotations;
4048
internal sealed class NotNullAttribute : Attribute { }
4149

4250
/// <summary>
43-
/// Describes dependency between method input and output.
51+
/// Describes dependence between method input and output.
4452
/// </summary>
4553
/// <syntax>
4654
/// <p>Function Definition Table syntax:</p>
@@ -51,22 +59,22 @@ internal sealed class NotNullAttribute : Attribute { }
5159
/// <item>Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
5260
/// <item>Value ::= true | false | null | notnull | canbenull</item>
5361
/// </list>
54-
/// If method has single input parameter, it's name could be omitted.<br/>
55-
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same) for method output
56-
/// means that the methos doesn't return normally (throws or terminates the process).<br/>
62+
/// If the method has a single input parameter, its name could be omitted.<br/>
63+
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same) for the method output
64+
/// means that the method doesn't return normally (throws or terminates the process).<br/>
5765
/// Value <c>canbenull</c> is only applicable for output parameters.<br/>
5866
/// You can use multiple <c>[ContractAnnotation]</c> for each FDT row, or use single attribute
59-
/// with rows separated by semicolon. There is no notion of order rows, all rows are checked
60-
/// for applicability and applied per each program state tracked by R# analysis.<br/>
67+
/// with rows separated by the semicolon. There is no notion of order rows, all rows are checked
68+
/// for applicability and applied per each program state tracked by the analysis engine.<br/>
6169
/// </syntax>
6270
/// <examples><list>
6371
/// <item><code>
6472
/// [ContractAnnotation("=&gt; halt")]
6573
/// public void TerminationMethod()
6674
/// </code></item>
6775
/// <item><code>
68-
/// [ContractAnnotation("halt &lt;= condition: false")]
69-
/// public void Assert(bool condition, string text) // regular assertion method
76+
/// [ContractAnnotation("null &lt;= param:null")] // reverse condition syntax
77+
/// public string GetName(string surname)
7078
/// </code></item>
7179
/// <item><code>
7280
/// [ContractAnnotation("s:null =&gt; true")]
@@ -76,7 +84,7 @@ internal sealed class NotNullAttribute : Attribute { }
7684
/// // A method that returns null if the parameter is null,
7785
/// // and not null if the parameter is not null
7886
/// [ContractAnnotation("null =&gt; null; notnull =&gt; notnull")]
79-
/// public object Transform(object data)
87+
/// public object Transform(object data)
8088
/// </code></item>
8189
/// <item><code>
8290
/// [ContractAnnotation("=&gt; true, result: notnull; =&gt; false, result: null")]
@@ -95,7 +103,26 @@ public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStat
95103
ForceFullStates = forceFullStates;
96104
}
97105

98-
[NotNull] public string Contract { get; private set; }
106+
[NotNull] public string Contract { get; }
107+
108+
public bool ForceFullStates { get; }
109+
}
99110

100-
public bool ForceFullStates { get; private set; }
101-
}
111+
/// <summary>
112+
/// Indicates that IEnumerable passed as a parameter is not enumerated.
113+
/// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection.
114+
/// </summary>
115+
/// <example><code>
116+
/// static void ThrowIfNull&lt;T&gt;([NoEnumeration] T v, string n) where T : class
117+
/// {
118+
/// // custom check for null but no enumeration
119+
/// }
120+
///
121+
/// void Foo(IEnumerable&lt;string&gt; values)
122+
/// {
123+
/// ThrowIfNull(values, nameof(values));
124+
/// var x = values.ToList(); // No warnings about multiple enumeration
125+
/// }
126+
/// </code></example>
127+
[AttributeUsage(AttributeTargets.Parameter)]
128+
internal sealed class NoEnumerationAttribute : Attribute { }

0 commit comments

Comments
 (0)