Skip to content

Commit bc278da

Browse files
committed
CA1822: Do not report on awaitable-awaiter
1 parent 9bea835 commit bc278da

File tree

5 files changed

+145
-1
lines changed

5 files changed

+145
-1
lines changed

src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/QualityGuidelines/MarkMembersAsStatic.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Collections.Generic;
44
using System.Collections.Immutable;
55
using System.Linq;
6-
using System.Threading;
76
using Analyzer.Utilities;
87
using Analyzer.Utilities.Extensions;
98
using Analyzer.Utilities.PooledObjects;
@@ -230,6 +229,24 @@ private static bool ShouldAnalyze(
230229
return false;
231230
}
232231

232+
// Awaitable-awaiter pattern members should not be marked as static.
233+
// There is no need to check for INotifyCompletion or ICriticalNotifyCompletion members as they are already excluded.
234+
if (wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesINotifyCompletion, out var inotifyCompletionType)
235+
&& wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesICriticalNotifyCompletion, out var icriticalNotifyCompletionType))
236+
{
237+
if (methodSymbol.IsGetAwaiterFromAwaitablePattern(inotifyCompletionType, icriticalNotifyCompletionType)
238+
|| methodSymbol.IsGetResultFromAwaiterPattern(inotifyCompletionType, icriticalNotifyCompletionType))
239+
{
240+
return false;
241+
}
242+
243+
if (methodSymbol.AssociatedSymbol is IPropertySymbol property
244+
&& property.IsIsCompletedFromAwaiterPattern(inotifyCompletionType, icriticalNotifyCompletionType))
245+
{
246+
return false;
247+
}
248+
}
249+
233250
var attributes = methodSymbol.GetAttributes();
234251
if (methodSymbol.AssociatedSymbol != null)
235252
{

src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/QualityGuidelines/MarkMembersAsStaticTests.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,69 @@ public void M() {}
12861286
}.RunAsync();
12871287
}
12881288

1289+
[Fact, WorkItem(4623, "https://github.com/dotnet/roslyn-analyzers/issues/4623")]
1290+
public async Task AwaiterPattern_INotifyCompletion_NoDiagnostic()
1291+
{
1292+
await VerifyCS.VerifyAnalyzerAsync(@"
1293+
using System;
1294+
using System.Runtime.CompilerServices;
1295+
1296+
public class DummyAwaiter : INotifyCompletion
1297+
{
1298+
public void GetResult()
1299+
{
1300+
}
1301+
1302+
public bool IsCompleted => false;
1303+
1304+
public void OnCompleted(Action continuation) => throw null;
1305+
}");
1306+
}
1307+
1308+
[Fact, WorkItem(4623, "https://github.com/dotnet/roslyn-analyzers/issues/4623")]
1309+
public async Task AwaiterPattern_ICriticalNotifyCompletion_NoDiagnostic()
1310+
{
1311+
await VerifyCS.VerifyAnalyzerAsync(@"
1312+
using System;
1313+
using System.Runtime.CompilerServices;
1314+
1315+
public class DummyAwaiter : ICriticalNotifyCompletion
1316+
{
1317+
public void GetResult()
1318+
{
1319+
}
1320+
1321+
public bool IsCompleted => false;
1322+
1323+
public void OnCompleted(Action continuation) => throw null;
1324+
public void UnsafeOnCompleted(Action continuation) => throw null;
1325+
}");
1326+
}
1327+
1328+
[Fact, WorkItem(4623, "https://github.com/dotnet/roslyn-analyzers/issues/4623")]
1329+
public async Task AwaitablePattern_NoDiagnostic()
1330+
{
1331+
await VerifyCS.VerifyAnalyzerAsync(@"
1332+
using System;
1333+
using System.Runtime.CompilerServices;
1334+
1335+
public class DummyAwaitable
1336+
{
1337+
public DummyAwaiter GetAwaiter() => new DummyAwaiter();
1338+
}
1339+
1340+
public class DummyAwaiter : INotifyCompletion
1341+
{
1342+
public void GetResult()
1343+
{
1344+
}
1345+
1346+
public bool IsCompleted => false;
1347+
1348+
public void OnCompleted(Action continuation) => throw null;
1349+
}");
1350+
}
1351+
12891352
private DiagnosticResult GetCSharpResultAt(int line, int column, string symbolName)
12901353
=> VerifyCS.Diagnostic()
12911354
.WithLocation(line, column)

src/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,5 +717,48 @@ public static bool IsTopLevelStatementsEntryPointMethod([NotNullWhen(true)] this
717717
"<Main>$" => true,
718718
_ => false
719719
};
720+
721+
public static bool IsGetAwaiterFromAwaitablePattern([NotNullWhen(true)] this IMethodSymbol? method,
722+
[NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType,
723+
[NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType)
724+
{
725+
if (method is null
726+
|| !method.Name.Equals("GetAwaiter", StringComparison.Ordinal)
727+
|| method.Parameters.Length != 0)
728+
{
729+
return false;
730+
}
731+
732+
var returnType = method.ReturnType?.OriginalDefinition;
733+
if (returnType is null)
734+
{
735+
return false;
736+
}
737+
738+
return returnType.DerivesFrom(inotifyCompletionType) ||
739+
returnType.DerivesFrom(icriticalNotifyCompletionType);
740+
}
741+
742+
public static bool IsGetResultFromAwaiterPattern(
743+
[NotNullWhen(true)] this IMethodSymbol? method,
744+
[NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType,
745+
[NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType)
746+
{
747+
if (method is null
748+
|| !method.Name.Equals("GetResult", StringComparison.Ordinal)
749+
|| method.Parameters.Length != 0)
750+
{
751+
return false;
752+
}
753+
754+
var containingType = method.ContainingType?.OriginalDefinition;
755+
if (containingType is null)
756+
{
757+
return false;
758+
}
759+
760+
return containingType.DerivesFrom(inotifyCompletionType) ||
761+
containingType.DerivesFrom(icriticalNotifyCompletionType);
762+
}
720763
}
721764
}

src/Utilities/Compiler/Extensions/IPropertySymbolExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
4+
using System.Diagnostics.CodeAnalysis;
35
using System.Linq;
46
using Microsoft.CodeAnalysis;
57

@@ -13,5 +15,22 @@ internal static class IPropertySymbolExtensions
1315
/// </summary>
1416
public static bool IsAutoProperty(this IPropertySymbol propertySymbol)
1517
=> propertySymbol.ContainingType.GetMembers().OfType<IFieldSymbol>().Any(f => f.IsImplicitlyDeclared && propertySymbol.Equals(f.AssociatedSymbol));
18+
19+
public static bool IsIsCompletedFromAwaiterPattern(
20+
[NotNullWhen(true)] this IPropertySymbol? property,
21+
[NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType,
22+
[NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType)
23+
{
24+
if (property is null
25+
|| !property.Name.Equals("IsCompleted", StringComparison.Ordinal)
26+
|| property.Type?.SpecialType != SpecialType.System_Boolean)
27+
{
28+
return false;
29+
}
30+
31+
var containingType = property.ContainingType?.OriginalDefinition;
32+
return containingType.DerivesFrom(inotifyCompletionType)
33+
|| containingType.DerivesFrom(icriticalNotifyCompletionType);
34+
}
1635
}
1736
}

src/Utilities/Compiler/WellKnownTypeNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ internal static class WellKnownTypeNames
251251
public const string SystemRuntimeCompilerServicesCallerMemberNameAttribute = "System.Runtime.CompilerServices.CallerMemberNameAttribute";
252252
public const string SystemRuntimeCompilerServicesCompilerGeneratedAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
253253
public const string SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable1 = "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1";
254+
public const string SystemRuntimeCompilerServicesICriticalNotifyCompletion = "System.Runtime.CompilerServices.ICriticalNotifyCompletion";
255+
public const string SystemRuntimeCompilerServicesINotifyCompletion = "System.Runtime.CompilerServices.INotifyCompletion";
254256
public const string SystemRuntimeCompilerServicesInternalsVisibleToAttribute = "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
255257
public const string SystemRuntimeCompilerServicesRestrictedInternalsVisibleToAttribute = "System.Runtime.CompilerServices.RestrictedInternalsVisibleToAttribute";
256258
public const string SystemRuntimeCompilerServicesTypeForwardedToAttribute = "System.Runtime.CompilerServices.TypeForwardedToAttribute";

0 commit comments

Comments
 (0)