Example #1
0
        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);
                }
            }
        }
Example #2
0
            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);
                    }
                }
            }