Пример #1
0
            public TypeSyntax Type0(Typ typ)
            {
                bool aliasable;

                if (!$"{Namespace}.".StartsWith($"{typ.Namespace}."))
                {
                    AddImport(typ.Namespace);
                    aliasable = false;
                }
                else
                {
                    aliasable = true;
                }
                SimpleNameSyntax result = IdentifierName(typ.Name);

                if (typ.IsDeprecated)
                {
                    result = result.WithIdentifier(result.Identifier.WithPragmaWarning(PragmaWarnings.Obsolete));
                }
                if (typ.GenericArgTyps != null)
                {
                    // Generic typ, so return a generic name by recursively calling this method on all type args.
                    result = GenericName(result.Identifier, TypeArgumentList(SeparatedList(typ.GenericArgTyps.Select(Type))));
                }
                if (aliasable)
                {
                    // Annotate this type to show it might need fully aliasing if there is a name collision.
                    result = result.WithAdditionalAnnotations(new SyntaxAnnotation(AliasableType, typ.Namespace));
                }
                return(result);
            }
Пример #2
0
 private static SimpleNameSyntax ChangeName(SimpleNameSyntax simpleName, string newName)
 {
     return(simpleName.WithIdentifier(
                Identifier(
                    simpleName.GetLeadingTrivia(),
                    newName,
                    simpleName.GetTrailingTrivia())));
 }
        private static Task <Document> RefactorAsync(
            Document document,
            SimpleNameSyntax simpleName,
            string newName,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier));

            return(document.ReplaceNodeAsync(simpleName, newNode, cancellationToken));
        }
        private CodeFixRegistrationResult ReplaceCountWithLengthOrViceVersa(
            CodeFixContext context,
            Diagnostic diagnostic,
            ExpressionSyntax expression,
            SimpleNameSyntax simpleName,
            SemanticModel semanticModel)
        {
            string name = simpleName.Identifier.ValueText;

            string newName = GetNewName();

            if (newName != null)
            {
                ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken);

                if (typeSymbol != null)
                {
                    if (typeSymbol is IArrayTypeSymbol arrayType)
                    {
                        typeSymbol = arrayType.ElementType;
                    }

                    foreach (ISymbol symbol in typeSymbol.GetMembers(newName))
                    {
                        if (!symbol.IsStatic &&
                            symbol.Kind == SymbolKind.Property)
                        {
                            var propertySymbol = (IPropertySymbol)symbol;

                            if (!propertySymbol.IsIndexer &&
                                propertySymbol.IsReadOnly &&
                                semanticModel.IsAccessible(expression.SpanStart, symbol))
                            {
                                Document document = context.Document;

                                CodeAction codeAction = CodeAction.Create(
                                    $"Use '{newName}' instead of '{name}'",
                                    ct =>
                                {
                                    SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier));

                                    return(document.ReplaceNodeAsync(simpleName, newNode, ct));
                                },
                                    GetEquivalenceKey(diagnostic));

                                context.RegisterCodeFix(codeAction, diagnostic);
                                return(new CodeFixRegistrationResult(true));
                            }
                        }
                    }
                }
            }

            return(default);
        private (ExpressionSyntax, SimpleNameSyntax) BuildNewSyntax(string newName, SimpleNameSyntax access)
        {
            var nameParts = newName.Split('.');

            Trace.Assert(nameParts.Length > 1);

            if (nameParts.Length == 2)
            {
                return(IdentifierName(nameParts[0]), access.WithIdentifier(Identifier(nameParts[1])));
            }

            var first = nameParts.First();

            // copy all but the first and last elements
            var middle = new string[nameParts.Length - 2];

            Array.Copy(nameParts, 1, middle, 0, middle.Length);

            var last = nameParts.Last();

            return(SyntaxFactoryUtils.MemberAccess(IdentifierName(first), middle), access.WithIdentifier(Identifier(last)));
        }
Пример #6
0
            private TypeSyntax Type0(Typ typ)
            {
                string namespaceAlias;

                if (!RequireFullyQualifiedTyp(typ) && $"{Namespace}.".StartsWith($"{typ.Namespace}."))
                {
                    // Within the current namespace and not required to force a fully-qualified name; no import required.
                    namespaceAlias = null;
                }
                else if (!_namespaceAliases.TryGetValue(typ.Namespace, out namespaceAlias))
                {
                    // A namespace that hasn't been seen before. Create an alias for it.
                    if (!_wellKnownNamespaceAliases.TryGetValue(typ.Namespace, out var rawAlias))
                    {
                        // If it's not a well-known namespace (e.g. "System"), then create an alias
                        // using the first character of each namespace part.
                        // TODO: Ensure single-character aliased are not generated; they cause a compilation error.
                        rawAlias = typ.Namespace
                                   .Split('.', StringSplitOptions.RemoveEmptyEntries)
                                   .Select(x => char.ToLowerInvariant(x[0]))
                                   .Aggregate("", (a, c) => a + c);
                    }
                    // Make sure all aliases are unique by adding a numeric suffix if necessary.
                    int index = 0;
                    namespaceAlias = rawAlias;
                    while (_namespaceAliasesOnly.Contains(namespaceAlias))
                    {
                        namespaceAlias = $"{rawAlias}{++index}";
                    }
                    // Record the alias for later.
                    _namespaceAliases.Add(typ.Namespace, namespaceAlias);
                    _namespaceAliasesOnly.Add(namespaceAlias);
                }
                SimpleNameSyntax result = IdentifierName(typ.Name);

                if (typ.IsDeprecated)
                {
                    result = result.WithIdentifier(result.Identifier.WithPragmaWarning(PragmaWarnings.Obsolete));
                }
                if (typ.GenericArgTyps != null)
                {
                    // Generic typ, so return a generic name by recursively calling this method on all type args.
                    result = GenericName(result.Identifier, TypeArgumentList(SeparatedList(typ.GenericArgTyps.Select(Type))));
                }
                // Return the final TypeSyntax, aliased or not as required.
                return(namespaceAlias == null ? (TypeSyntax)result : AliasQualifiedName(namespaceAlias, result));
            }
            protected override async Task <Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
            {
                var document = this.document;
                var 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 == null)
                {
                    var 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.
                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

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

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

                if (originalAnonymousMethodContainerIfApplicable != null)
                {
                    updatedMethod = originalMethodDeclaration.ReplaceNode(
                        originalAnonymousMethodContainerIfApplicable,
                        originalAnonymousMethodContainerIfApplicable.MakeMethodAsync(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();
                }

                var syncExpression = (ExpressionSyntax)syncMethodName.FirstAncestorOrSelf <InvocationExpressionSyntax>() ?? syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>();

                ExpressionSyntax awaitExpression;

                if (this.AlternativeAsyncMethod != string.Empty)
                {
                    // Replace the member being called and await the invocation expression.
                    // While doing so, move leading trivia to the surrounding await expression.
                    var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[AsyncMethodKeyName]));
                    awaitExpression = SyntaxFactory.AwaitExpression(
                        syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia())
                                      .WithLeadingTrivia(syncExpression.GetLeadingTrivia());
                    if (!(syncExpression.Parent is ExpressionStatementSyntax))
                    {
                        awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression)
                                          .WithAdditionalAnnotations(Simplifier.Annotation);
                    }
                }
                else
                {
                    // Remove the member being accessed that causes a synchronous block and simply await the object.
                    var syncMemberAccess             = syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>();
                    var 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());
                }

                updatedMethod = updatedMethod
                                .ReplaceNode(syncExpression, awaitExpression);

                var newRoot     = root.ReplaceNode(originalMethodDeclaration, updatedMethod);
                var newDocument = document.WithSyntaxRoot(newRoot);

                return(newDocument.Project.Solution);
            }
Пример #8
0
        private async Task ComputeCodeFixAsync(
            CodeFixContext context,
            Diagnostic diagnostic,
            ExpressionSyntax expression,
            SimpleNameSyntax simpleName)
        {
            string name = simpleName.Identifier.ValueText;

            if (name != "Count" &&
                name != "Length")
            {
                return;
            }

            string newName = (name == "Count") ? "Length" : "Count";

            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

            if (!IsFixable())
            {
                return;
            }

            CodeAction codeAction = CodeAction.Create(
                $"Use '{newName}' instead of '{name}'",
                cancellationToken =>
            {
                SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier));

                return(context.Document.ReplaceNodeAsync(simpleName, newNode, cancellationToken));
            },
                GetEquivalenceKey(diagnostic));

            context.RegisterCodeFix(codeAction, diagnostic);

            bool IsFixable()
            {
                ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken);

                if (typeSymbol != null)
                {
                    if (typeSymbol is IArrayTypeSymbol arrayType)
                    {
                        typeSymbol = arrayType.ElementType;
                    }

                    foreach (ISymbol symbol in typeSymbol.GetMembers(newName))
                    {
                        if (!symbol.IsStatic &&
                            symbol.Kind == SymbolKind.Property)
                        {
                            var propertySymbol = (IPropertySymbol)symbol;

                            if (!propertySymbol.IsIndexer &&
                                propertySymbol.IsReadOnly &&
                                semanticModel.IsAccessible(expression.SpanStart, symbol))
                            {
                                return(true);
                            }
                        }
                    }
                }

                return(false);
            }
        }