コード例 #1
0
        private bool IsContractInvocation(
            InvocationExpressionSyntax invocationExpression,
            ContractMethodNames allowedMethodNames,
            INamedTypeSymbol contractTypeSymbol)
        {
            MemberAccessExpressionSyntax?memberAccess =
                invocationExpression
                .Expression.As(x => x as MemberAccessExpressionSyntax);

            if ((ParseContractMethodName(memberAccess?.Name.Identifier.Text) & allowedMethodNames) == ContractMethodNames.None)
            {
                return(false);
            }

            var memberSymbol =
                memberAccess
                ?.As(x => _semanticModel.GetSymbolInfo(x).Symbol as IMethodSymbol);

            // TODO: ToMetadataFullName() on every call is probably somewhat expensive
            if (memberSymbol == null || !memberSymbol.ContainingType.Equals(contractTypeSymbol))
            {
                // This is not Contract.
                return(false);
            }

            return(true);
        }
コード例 #2
0
        internal static void InspectMemberAccess(
            SyntaxNodeAnalysisContext context,
            MemberAccessExpressionSyntax?memberAccessSyntax,
            DiagnosticDescriptor descriptor,
            IEnumerable <CommonInterest.SyncBlockingMethod> problematicMethods,
            bool ignoreIfInsideAnonymousDelegate = false)
        {
            if (descriptor is null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            if (memberAccessSyntax is null)
            {
                return;
            }

            if (ShouldIgnoreContext(context))
            {
                return;
            }

            if (ignoreIfInsideAnonymousDelegate && context.Node.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>() is object)
            {
                // We do not analyze JTF.Run inside anonymous functions because
                // they are so often used as callbacks where the signature is constrained.
                return;
            }

            if (CSharpUtils.IsWithinNameOf(context.Node as ExpressionSyntax))
            {
                // We do not consider arguments to nameof( ) because they do not represent invocations of code.
                return;
            }

            ITypeSymbol?typeReceiver = context.SemanticModel.GetTypeInfo(memberAccessSyntax.Expression).Type;

            if (typeReceiver is object)
            {
                foreach (CommonInterest.SyncBlockingMethod item in problematicMethods)
                {
                    if (memberAccessSyntax.Name.Identifier.Text == item.Method.Name &&
                        typeReceiver.Name == item.Method.ContainingType.Name &&
                        typeReceiver.BelongsToNamespace(item.Method.ContainingType.Namespace))
                    {
                        if (HasTaskCompleted(context, memberAccessSyntax))
                        {
                            return;
                        }

                        Location?location = memberAccessSyntax.Name.GetLocation();
                        context.ReportDiagnostic(Diagnostic.Create(descriptor, location));
                    }
                }
            }
        }
        private static void ReplaceMethodCall(MemberAccessExpressionSyntax?memberExpression, DocumentEditor docEditor)
        {
            if (memberExpression is null)
            {
                return;
            }

            var parsedExpression     = ParseExpression("Deserialize");
            var identifierExpression = memberExpression.DescendantNodes().OfType <IdentifierNameSyntax>().Last();

            docEditor.ReplaceNode(identifierExpression, parsedExpression);
        }
        private static void DropNullParameter(MemberAccessExpressionSyntax?memberExpression, DocumentEditor docEditor)
        {
            if (memberExpression is null || memberExpression.Parent is null)
            {
                return;
            }

            var argumentListSyntaxOriginal = memberExpression.Parent.DescendantNodes().OfType <ArgumentListSyntax>().Last();
            var modified = argumentListSyntaxOriginal.WithArguments(argumentListSyntaxOriginal.Arguments.RemoveAt(1));

            docEditor.ReplaceNode(argumentListSyntaxOriginal, modified);
        }
コード例 #5
0
        private void InspectMemberAccess(
            SyntaxNodeAnalysisContext context,
            MemberAccessExpressionSyntax?memberAccessSyntax,
            IEnumerable <CommonInterest.SyncBlockingMethod> problematicMethods,
            INamedTypeSymbol taskSymbol)
        {
            if (memberAccessSyntax == null)
            {
                return;
            }

            // Are we in the context of an anonymous function that is passed directly in as an argument to another method?
            var anonymousFunctionSyntax     = context.Node.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>();
            var anonFuncAsArgument          = anonymousFunctionSyntax?.Parent as ArgumentSyntax;
            var invocationPassingExpression = anonFuncAsArgument?.Parent?.Parent as InvocationExpressionSyntax;
            var invokedMemberAccess         = invocationPassingExpression?.Expression as MemberAccessExpressionSyntax;

            if (invokedMemberAccess?.Name != null)
            {
                // Does the anonymous function appear as the first argument to Task.ContinueWith?
                var invokedMemberSymbol = context.SemanticModel.GetSymbolInfo(invokedMemberAccess.Name, context.CancellationToken).Symbol as IMethodSymbol;
                if (invokedMemberSymbol?.Name == nameof(Task.ContinueWith) &&
                    Utils.IsEqualToOrDerivedFrom(invokedMemberSymbol?.ContainingType, taskSymbol) &&
                    invocationPassingExpression?.ArgumentList?.Arguments.FirstOrDefault() == anonFuncAsArgument)
                {
                    // Does the member access being analyzed belong to the Task that just completed?
                    var firstParameter = GetFirstParameter(anonymousFunctionSyntax);
                    if (firstParameter != null)
                    {
                        // Are we accessing a member of the completed task?
                        ISymbol          invokedObjectSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax.Expression, context.CancellationToken).Symbol;
                        IParameterSymbol completedTask       = context.SemanticModel.GetDeclaredSymbol(firstParameter);
                        if (EqualityComparer <ISymbol> .Default.Equals(invokedObjectSymbol, completedTask))
                        {
                            // Skip analysis since Task.Result (et. al) of a completed Task is fair game.
                            return;
                        }
                    }
                }
            }

            CSharpCommonInterest.InspectMemberAccess(context, memberAccessSyntax, Descriptor, problematicMethods);
        }
コード例 #6
0
            private void InspectMemberAccess(SyntaxNodeAnalysisContext context, MemberAccessExpressionSyntax?memberAccessSyntax, IEnumerable <CommonInterest.SyncBlockingMethod> problematicMethods)
            {
                if (memberAccessSyntax == null)
                {
                    return;
                }

                if (this.diagnosticReported)
                {
                    // Don't report more than once per method.
                    return;
                }

                if (context.Node.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>() != null)
                {
                    // We do not analyze JTF.Run inside anonymous functions because
                    // they are so often used as callbacks where the signature is constrained.
                    return;
                }

                if (Utils.IsWithinNameOf(context.Node as ExpressionSyntax))
                {
                    // We do not consider arguments to nameof( ) because they do not represent invocations of code.
                    return;
                }

                var invokedMember = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;

                if (invokedMember != null)
                {
                    foreach (var item in problematicMethods)
                    {
                        if (item.Method.IsMatch(invokedMember))
                        {
                            var location = memberAccessSyntax.Name.GetLocation();
                            context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
                            this.diagnosticReported = true;
                        }
                    }
                }
            }
コード例 #7
0
        private static bool IsTaskCompletedWithWhenAll(SyntaxNodeAnalysisContext context, InvocationExpressionSyntax invocationExpr, string taskVariableName)
        {
            // We only care about awaited invocations, because an un-awaited Task.WhenAll will be an error.
            if (invocationExpr.Parent is not AwaitExpressionSyntax)
            {
                return(false);
            }

            IEnumerable <MemberAccessExpressionSyntax>?memberAccessList = invocationExpr.ChildNodes().OfType <MemberAccessExpressionSyntax>();

            if (memberAccessList.Count() != 1)
            {
                return(false);
            }

            MemberAccessExpressionSyntax?memberAccess = memberAccessList.First();

            // Does the invocation have the expected `Task.WhenAll` syntax? This is cheaper to verify before looking up its semantic type.
            var correctSyntax =
                ((IdentifierNameSyntax)memberAccess.Expression).Identifier.ValueText == Types.Task.TypeName &&
                ((IdentifierNameSyntax)memberAccess.Name).Identifier.ValueText == Types.Task.WhenAll;

            if (!correctSyntax)
            {
                return(false);
            }

            // Is this `Task.WhenAll` invocation from the System.Threading.Tasks.Task type?
            ITypeSymbol?classType   = context.SemanticModel.GetTypeInfo(memberAccess.Expression).Type;
            var         correctType = classType.Name == Types.Task.TypeName && classType.BelongsToNamespace(Types.Task.Namespace);

            if (!correctType)
            {
                return(false);
            }

            // Is the task variable passed as an argument to `Task.WhenAll`?
            return(IsVariablePassedToInvocation(invocationExpr, taskVariableName, byRef: false));
        }
コード例 #8
0
            private static bool InspectMemberAccess(SyntaxNodeAnalysisContext context, [NotNullWhen(true)] MemberAccessExpressionSyntax?memberAccessSyntax, IEnumerable <CommonInterest.SyncBlockingMethod> problematicMethods)
            {
                if (memberAccessSyntax == null)
                {
                    return(false);
                }

                var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;

                if (memberSymbol != null)
                {
                    foreach (var item in problematicMethods)
                    {
                        if (item.Method.IsMatch(memberSymbol))
                        {
                            var location   = memberAccessSyntax.Name.GetLocation();
                            var properties = ImmutableDictionary <string, string> .Empty
                                             .Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace != null?string.Join(".", item.ExtensionMethodNamespace) : string.Empty);

                            DiagnosticDescriptor descriptor;
                            var messageArgs = new List <object>(2);
                            messageArgs.Add(item.Method.Name);
                            if (item.AsyncAlternativeMethodName != null)
                            {
                                properties = properties.Add(AsyncMethodKeyName, item.AsyncAlternativeMethodName);
                                descriptor = Descriptor;
                                messageArgs.Add(item.AsyncAlternativeMethodName);
                            }
                            else
                            {
                                properties = properties.Add(AsyncMethodKeyName, string.Empty);
                                descriptor = DescriptorNoAlternativeMethod;
                            }

                            Diagnostic diagnostic = Diagnostic.Create(descriptor, location, properties, messageArgs.ToArray());
                            context.ReportDiagnostic(diagnostic);
                            return(true);
                        }
                    }
                }

                return(false);
            }
コード例 #9
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            if (root is null)
            {
                return;
            }

            context.CancellationToken.ThrowIfCancellationRequested();

            var node = root.FindNode(context.Span);

            if (node is not ArgumentSyntax actualArgument ||
                actualArgument.Expression is not MemberAccessExpressionSyntax actualExpression ||
                actualArgument.Parent is not ArgumentListSyntax argumentList ||
                argumentList.Arguments.Count <= 1 ||
                argumentList.Arguments[1] is not ArgumentSyntax constraintArgument)
            {
                return;
            }

            // We have either a MemberAccessExpression (Is.Zero) or an InvocationExpression (Is.EqualTo(0))
            var constraintMemberExpression = constraintArgument.Expression as MemberAccessExpressionSyntax;
            var constraintExpression       = constraintArgument.Expression as InvocationExpressionSyntax;

            if (constraintExpression is not null)
            {
                constraintMemberExpression = constraintExpression.Expression as MemberAccessExpressionSyntax;
            }

            if (constraintMemberExpression is null)
            {
                return;
            }

            ExpressionSyntax innerConstraintExpression = FindLeftMostTerm(constraintMemberExpression);

            if (innerConstraintExpression is not SimpleNameSyntax simple || simple.Identifier.ValueText != NUnitFrameworkConstants.NameOfIs)
            {
                return;
            }

            var nodesToReplace = new Dictionary <SyntaxNode, SyntaxNode>()
            {
                // Replace <expression>.<property> with <expression> in first argument
                { actualArgument.Expression, actualExpression.Expression }
            };

            string description;

            MemberAccessExpressionSyntax?emptyOrNotEmptyExpression = MatchWithEmpty(constraintMemberExpression,
                                                                                    constraintExpression, innerConstraintExpression);

            if (emptyOrNotEmptyExpression is not null &&
                await IsCollectionType(context, actualExpression.Expression).ConfigureAwait(false))
            {
                nodesToReplace.Add(constraintArgument.Expression, emptyOrNotEmptyExpression);

                description = $"Use {emptyOrNotEmptyExpression}";
            }
コード例 #10
0
            protected override async Task <Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
            {
                Document?  document = this.document;
                SyntaxNode?root     = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                // Find the synchronously blocking call member,
                // and bookmark it so we can find it again after some mutations have taken place.
                var syncAccessBookmark          = new SyntaxAnnotation();
                SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan);

                if (syncMethodName is null)
                {
                    MemberAccessExpressionSyntax?syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf <MemberAccessExpressionSyntax>();
                    syncMethodName = syncMemberAccess.Name;
                }

                // When we give the Document a modified SyntaxRoot, yet another is created. So we first assign it to the Document,
                // then we query for the SyntaxRoot from the Document.
                document = document.WithSyntaxRoot(
                    root.ReplaceNode(syncMethodName, syncMethodName.WithAdditionalAnnotations(syncAccessBookmark)));
                root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                syncMethodName = (SimpleNameSyntax)root.GetAnnotatedNodes(syncAccessBookmark).Single();

                // We'll need the semantic model later. But because we've annotated a node, that changes the SyntaxRoot
                // and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!).
                // So after acquiring the semantic model, update it with the new method body.
                SemanticModel?semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                AnonymousFunctionExpressionSyntax?originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>();
                MethodDeclarationSyntax?          originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf <MethodDeclarationSyntax>();

                ISymbol?enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken);
                var     hasReturnValue  = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false;

                // Ensure that the method or anonymous delegate is using the async keyword.
                MethodDeclarationSyntax updatedMethod;

                if (originalAnonymousMethodContainerIfApplicable is object)
                {
                    updatedMethod = originalMethodDeclaration.ReplaceNode(
                        originalAnonymousMethodContainerIfApplicable,
                        originalAnonymousMethodContainerIfApplicable.MakeMethodAsync(hasReturnValue, semanticModel, cancellationToken));
                }
                else
                {
                    (document, updatedMethod) = await originalMethodDeclaration.MakeMethodAsync(document, cancellationToken).ConfigureAwait(false);

                    semanticModel = null; // out-dated
                }

                if (updatedMethod != originalMethodDeclaration)
                {
                    // Re-discover our synchronously blocking member.
                    syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single();
                }

                ExpressionSyntax?syncExpression = GetSynchronousExpression(syncMethodName);

                ExpressionSyntax awaitExpression;

                if (!string.IsNullOrEmpty(this.AlternativeAsyncMethod))
                {
                    // Replace the member being called and await the invocation expression.
                    // While doing so, move leading trivia to the surrounding await expression.
                    SimpleNameSyntax?asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName]));
                    awaitExpression = SyntaxFactory.AwaitExpression(
                        syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia())
                                      .WithLeadingTrivia(syncExpression.GetLeadingTrivia());
                }
                else
                {
                    // Remove the member being accessed that causes a synchronous block and simply await the object.
                    MemberAccessExpressionSyntax?syncMemberAccess             = syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>();
                    ExpressionSyntax?            syncMemberStrippedExpression = syncMemberAccess.Expression;

                    // Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls.
                    var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax;
                    if (expressionMethodCall?.Name.Identifier.Text == nameof(Task.GetAwaiter))
                    {
                        syncMemberStrippedExpression = expressionMethodCall.Expression;
                    }

                    awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression.WithoutLeadingTrivia())
                                      .WithLeadingTrivia(syncMemberStrippedExpression.GetLeadingTrivia());
                }

                if (!(syncExpression.Parent is ExpressionStatementSyntax))
                {
                    awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression)
                                      .WithAdditionalAnnotations(Simplifier.Annotation);
                }

                updatedMethod = updatedMethod
                                .ReplaceNode(syncExpression, awaitExpression);

                SyntaxNode?newRoot     = root.ReplaceNode(originalMethodDeclaration, updatedMethod);
                Document?  newDocument = document.WithSyntaxRoot(newRoot);

                return(newDocument.Project.Solution);
            }