Skip to content

Commit

Permalink
Merge pull request #3972 from Evangelink/CA1024-task
Browse files Browse the repository at this point in the history
CA1024: Do not report on tasks return types
  • Loading branch information
mavasani authored Aug 7, 2020
2 parents 9e2e2f1 + bb4069e commit 2208884
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,47 +42,63 @@ public override void Initialize(AnalysisContext analysisContext)
analysisContext.EnableConcurrentExecution();
analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

analysisContext.RegisterOperationBlockStartAction(context =>
analysisContext.RegisterCompilationStartAction(context =>
{
var taskTypesBuilder = ImmutableHashSet.CreateBuilder<ITypeSymbol>();

if (!(context.OwningSymbol is IMethodSymbol methodSymbol) ||
methodSymbol.ReturnsVoid ||
methodSymbol.ReturnType.Kind == SymbolKind.ArrayType ||
!methodSymbol.Parameters.IsEmpty ||
!methodSymbol.MatchesConfiguredVisibility(context.Options, Rule, context.Compilation, context.CancellationToken) ||
methodSymbol.IsAccessorMethod() ||
!IsPropertyLikeName(methodSymbol.Name))
{
return;
}
taskTypesBuilder.AddIfNotNull(
context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask));
taskTypesBuilder.AddIfNotNull(
context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1));
taskTypesBuilder.AddIfNotNull(
context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask));
taskTypesBuilder.AddIfNotNull(
context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask1));

// A few additional checks to reduce the noise for this diagnostic:
// Ensure that the method is non-generic, non-virtual/override, has no overloads and doesn't have special names: 'GetHashCode' or 'GetEnumerator'.
// Also avoid generating this diagnostic if the method body has any invocation expressions.
// Also avoid implicit interface implementation (explicit are handled through the member accessibility)
if (methodSymbol.IsGenericMethod ||
methodSymbol.IsVirtual ||
methodSymbol.IsOverride ||
methodSymbol.ContainingType.GetMembers(methodSymbol.Name).Length > 1 ||
methodSymbol.Name == GetHashCodeName ||
methodSymbol.Name == GetEnumeratorName ||
methodSymbol.IsImplementationOfAnyImplicitInterfaceMember())
{
return;
}
var taskTypes = taskTypesBuilder.ToImmutable();

bool hasInvocations = false;
context.RegisterOperationAction(operationContext =>
context.RegisterOperationBlockStartAction(context =>
{
hasInvocations = true;
}, OperationKind.Invocation);
if (!(context.OwningSymbol is IMethodSymbol methodSymbol) ||
methodSymbol.ReturnsVoid ||
methodSymbol.ReturnType.Kind == SymbolKind.ArrayType ||
!methodSymbol.Parameters.IsEmpty ||
!methodSymbol.MatchesConfiguredVisibility(context.Options, Rule, context.Compilation, context.CancellationToken) ||
methodSymbol.IsAccessorMethod() ||
!IsPropertyLikeName(methodSymbol.Name))
{
return;
}

context.RegisterOperationBlockEndAction(endContext =>
{
if (!hasInvocations)
// A few additional checks to reduce the noise for this diagnostic:
// Ensure that the method is non-generic, non-virtual/override, has no overloads and doesn't have special names: 'GetHashCode' or 'GetEnumerator'.
// Also avoid generating this diagnostic if the method body has any invocation expressions.
// Also avoid implicit interface implementation (explicit are handled through the member accessibility)
if (methodSymbol.IsGenericMethod ||
methodSymbol.IsVirtual ||
methodSymbol.IsOverride ||
methodSymbol.Name == GetHashCodeName ||
methodSymbol.Name == GetEnumeratorName ||
methodSymbol.ContainingType.GetMembers(methodSymbol.Name).Length > 1 ||
taskTypes.Contains(methodSymbol.ReturnType.OriginalDefinition) ||
methodSymbol.IsImplementationOfAnyImplicitInterfaceMember())
{
endContext.ReportDiagnostic(endContext.OwningSymbol.CreateDiagnostic(Rule));
return;
}

bool hasInvocations = false;
context.RegisterOperationAction(operationContext =>
{
hasInvocations = true;
}, OperationKind.Invocation);

context.RegisterOperationBlockEndAction(endContext =>
{
if (!hasInvocations)
{
endContext.ReportDiagnostic(endContext.OwningSymbol.CreateDiagnostic(Rule));
}
});
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,45 @@ public object GetContent()
");
}

[Fact, WorkItem(3877, "https://github.com/dotnet/roslyn-analyzers/issues/3877")]
public async Task CA1024_ReturnsTask_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System.Threading.Tasks;
public class Something
{
public Task GetTask() => default(Task);
public Task<int> GetGenericTask() => default(Task<int>);
public ValueTask GetValueTask() => default(ValueTask);
public ValueTask<int> GetGenericValueTask() => default(ValueTask<int>);
}
");

await VerifyVB.VerifyAnalyzerAsync(@"
Imports System.Threading.Tasks
Public Class Something
Public Function GetTask() As Task
Return Nothing
End Function
Public Function GetGenericTask() As Task(Of Integer)
Return Nothing
End Function
Public Function GetValueTask() As ValueTask
Return Nothing
End Function
Public Function GetGenericValueTask() As ValueTask(Of Integer)
Return Nothing
End Function
End Class
");
}

private static DiagnosticResult GetCA1024CSharpResultAt(int line, int column, string methodName)
=> VerifyCS.Diagnostic()
.WithLocation(line, column)
Expand Down

0 comments on commit 2208884

Please sign in to comment.