internal void AnalyzeInvocation(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; var targetMethod = operation.TargetMethod; if (IsTaskSymbol(targetMethod.ReturnType)) { return; } if (operation.IsInNameofOperation()) { return; } // Process.WaitForExit => Skip because the async method is not equivalent https://github.com/dotnet/runtime/issues/42556 if (string.Equals(targetMethod.Name, nameof(System.Diagnostics.Process.WaitForExit), StringComparison.Ordinal) && targetMethod.ContainingType.IsEqualTo(ProcessSymbol)) { if (targetMethod.ContainingType.ContainingAssembly.Identity.Version < new Version(6, 0, 0, 0)) { return; } } // Task.Wait() // Task`1.Wait() if (string.Equals(targetMethod.Name, nameof(Task.Wait), StringComparison.Ordinal)) { if (targetMethod.ContainingType.OriginalDefinition.IsEqualToAny(TaskSymbol, TaskOfTSymbol)) { ReportDiagnosticIfNeeded(context, operation, "Use await instead of 'Wait()'"); return; } } // Task.GetAwaiter().GetResult() if (string.Equals(targetMethod.Name, nameof(TaskAwaiter.GetResult), StringComparison.Ordinal)) { if (targetMethod.ContainingType.OriginalDefinition.IsEqualToAny(TaskAwaiterSymbol, TaskAwaiterOfTSymbol, ValueTaskAwaiterSymbol, ValueTaskAwaiterOfTSymbol)) { ReportDiagnosticIfNeeded(context, operation, "Use await instead of 'GetResult()'"); return; } } // Thread.Sleep => Task.Delay if (string.Equals(targetMethod.Name, "Sleep", StringComparison.Ordinal)) { if (targetMethod.ContainingType.IsEqualToAny(ThreadSymbols)) { ReportDiagnosticIfNeeded(context, operation, "Use await and 'Task.Delay()' instead of 'Thread.Sleep()'"); return; } } // Console.Out|Error.Write if (string.Equals(targetMethod.Name, "Write", StringComparison.Ordinal) || string.Equals(targetMethod.Name, "WriteLine", StringComparison.Ordinal) || string.Equals(targetMethod.Name, "Flush", StringComparison.Ordinal)) { var left = operation.Children.FirstOrDefault(); if (left is IMemberReferenceOperation memberReference) { if (ConsoleErrorAndOutSymbols.Contains(memberReference.Member, SymbolEqualityComparer.Default)) { return; } } } if (ServiceProviderServiceExtensions_CreateAsyncScopeSymbol != null && ServiceProviderServiceExtensions_CreateScopeSymbol != null && targetMethod.IsEqualTo(ServiceProviderServiceExtensions_CreateScopeSymbol)) { ReportDiagnosticIfNeeded(context, operation, $"Use 'CreateAsyncScope' instead of '{targetMethod.Name}'"); return; } // Search async equivalent: sample.Write() => sample.WriteAsync() if (!targetMethod.ReturnType.OriginalDefinition.IsEqualToAny(TaskSymbol, TaskOfTSymbol)) { var position = operation.Syntax.GetLocation().SourceSpan.End; var potentionalMethods = new List <ISymbol>(); potentionalMethods.AddRange(operation.SemanticModel !.LookupSymbols(position, targetMethod.ContainingType, name: targetMethod.Name, includeReducedExtensionMethods: true)); if (!targetMethod.Name.EndsWith("Async", StringComparison.Ordinal)) { potentionalMethods.AddRange(operation.SemanticModel.LookupSymbols(position, targetMethod.ContainingType, name: targetMethod.Name + "Async", includeReducedExtensionMethods: true)); } var potentialMethod = potentionalMethods.FirstOrDefault(IsPotentialMember); if (potentialMethod != null) { ReportDiagnosticIfNeeded(context, operation, $"Use '{potentialMethod.Name}' instead of '{targetMethod.Name}'"); } bool IsPotentialMember(ISymbol memberSymbol) { if (memberSymbol.IsEqualTo(targetMethod)) { return(false); } if (memberSymbol is IMethodSymbol methodSymbol) { if (targetMethod.IsStatic && !methodSymbol.IsStatic) { return(false); } if (!IsTaskSymbol(methodSymbol.ReturnType)) { return(false); } if (methodSymbol.IsObsolete(context.Compilation)) { return(false); } if (!targetMethod.HasSimilarParameters(methodSymbol) && !targetMethod.HasSimilarParameters(methodSymbol, CancellationTokenSymbol)) { return(false); } return(true); } return(false); } } }
internal void AnalyzeInvocation(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; var targetMethod = operation.TargetMethod; if (IsTaskSymbol(targetMethod.ReturnType)) { return; } if (operation.IsInNameofOperation()) { return; } // Process.WaitForExit => Skip because the async method is not equivalent https://github.com/dotnet/runtime/issues/42556 if (string.Equals(targetMethod.Name, nameof(System.Diagnostics.Process.WaitForExit), StringComparison.Ordinal) && targetMethod.ContainingType.OriginalDefinition.IsEqualTo(ProcessSymbol)) { return; } // Task.Wait() // Task`1.Wait() if (string.Equals(targetMethod.Name, nameof(Task.Wait), StringComparison.Ordinal)) { if (targetMethod.ContainingType.OriginalDefinition.IsEqualToAny(TaskSymbol, TaskOfTSymbol)) { ReportDiagnosticIfNeeded(context, operation, "Use await instead of 'Wait()'"); return; } } // Task.GetAwaiter().GetResult() if (string.Equals(targetMethod.Name, nameof(TaskAwaiter.GetResult), StringComparison.Ordinal)) { if (targetMethod.ContainingType.OriginalDefinition.IsEqualToAny(TaskAwaiterSymbol, TaskAwaiterOfTSymbol, ValueTaskAwaiterSymbol, ValueTaskAwaiterOfTSymbol)) { ReportDiagnosticIfNeeded(context, operation, "Use await instead of 'GetResult()'"); return; } } // Thread.Sleep => Task.Delay if (string.Equals(targetMethod.Name, "Sleep", StringComparison.Ordinal)) { if (targetMethod.ContainingType.IsEqualToAny(ThreadSymbols)) { ReportDiagnosticIfNeeded(context, operation, "Use await and 'Task.Delay()' instead of 'Thread.Sleep()'"); return; } } // Console.Out|Error.Write if (string.Equals(targetMethod.Name, "Write", StringComparison.Ordinal) || string.Equals(targetMethod.Name, "WriteLine", StringComparison.Ordinal) || string.Equals(targetMethod.Name, "Flush", StringComparison.Ordinal)) { var left = operation.Children.FirstOrDefault(); if (left is IMemberReferenceOperation memberReference) { if (ConsoleErrorAndOutSymbols.Contains(memberReference.Member, SymbolEqualityComparer.Default)) { return; } } } // Search async equivalent: sample.Write() => sample.WriteAsync() if (!targetMethod.ReturnType.OriginalDefinition.IsEqualToAny(TaskSymbol, TaskOfTSymbol)) { var potentialMethod = targetMethod.ContainingType.GetMembers().FirstOrDefault(IsPotentialMember); if (potentialMethod != null) { ReportDiagnosticIfNeeded(context, operation, $"Use '{potentialMethod.Name}' instead of '{targetMethod.Name}'"); } bool IsPotentialMember(ISymbol memberSymbol) { if (memberSymbol.IsEqualTo(targetMethod)) { return(false); } if (memberSymbol is IMethodSymbol methodSymbol) { if (targetMethod.IsStatic && !methodSymbol.IsStatic) { return(false); } if (!string.Equals(methodSymbol.Name, targetMethod.Name, StringComparison.Ordinal) && !string.Equals(methodSymbol.Name, targetMethod.Name + "Async", StringComparison.Ordinal)) { return(false); } if (!IsTaskSymbol(methodSymbol.ReturnType)) { return(false); } if (methodSymbol.IsObsolete(context.Compilation)) { return(false); } if (!targetMethod.HasSimilarParameters(methodSymbol) && !targetMethod.HasSimilarParameters(methodSymbol, CancellationTokenSymbol)) { return(false); } return(true); } return(false); } } }