Example #1
0
        private static void ReplaceSetReferences(
            string propertyName, bool nameChanged,
            IEnumerable <ReferenceLocation> setReferences,
            SyntaxNode root, SyntaxEditor editor,
            IReplaceMethodWithPropertyService service,
            CancellationToken cancellationToken)
        {
            if (setReferences != null)
            {
                foreach (var referenceLocation in setReferences)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var location  = referenceLocation.Location;
                    var nameToken = root.FindToken(location.SourceSpan.Start);

                    if (referenceLocation.IsImplicit)
                    {
                        // Warn the user that we can't properly replace this method with a property.
                        editor.ReplaceNode(nameToken.Parent, nameToken.Parent.WithAdditionalAnnotations(
                                               ConflictAnnotation.Create(FeaturesResources.Method_referenced_implicitly)));
                    }
                    else
                    {
                        service.ReplaceSetReference(editor, nameToken, propertyName, nameChanged);
                    }
                }
            }
        }
        public void ReplaceInvocation(SyntaxEditor editor, SyntaxToken nameToken, string propertyName, bool nameChanged,
                                      Action <SyntaxEditor, InvocationExpressionSyntax, SimpleNameSyntax, SimpleNameSyntax> replace)
        {
            if (nameToken.Kind() != SyntaxKind.IdentifierToken)
            {
                return;
            }

            var nameNode = nameToken.Parent as IdentifierNameSyntax;

            if (nameNode == null)
            {
                return;
            }

            var newName = nameChanged
                ? SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(propertyName).WithTriviaFrom(nameToken))
                : nameNode;

            var invocation           = nameNode?.FirstAncestorOrSelf <InvocationExpressionSyntax>();
            var invocationExpression = invocation?.Expression;

            if (!IsInvocationName(nameNode, invocationExpression))
            {
                // Wasn't invoked.  Change the name, but report a conflict.
                var annotation = ConflictAnnotation.Create(FeaturesResources.NonInvokedMethodCannotBeReplacedWithProperty);
                editor.ReplaceNode(nameNode, newName.WithIdentifier(newName.Identifier.WithAdditionalAnnotations(annotation)));
                return;
            }

            // It was invoked.  Remove the invocation, and also change the name if necessary.
            replace(editor, invocation, nameNode, newName);
        }
            private static SyntaxToken AddConflictAnnotation(SyntaxToken token, string?conflictMessage)
            {
                if (conflictMessage != null)
                {
                    token = token.WithAdditionalAnnotations(ConflictAnnotation.Create(conflictMessage));
                }

                return(token);
            }
Example #4
0
            /// <summary>
            /// If the statement has an `out var` declaration expression for a variable which
            /// needs to be removed, we need to turn it into a plain `out` parameter, so that
            /// it doesn't declare a duplicate variable.
            /// If the statement has a pattern declaration (such as `3 is int i`) for a variable
            /// which needs to be removed, we will annotate it as a conflict, since we don't have
            /// a better refactoring.
            /// </summary>
            private StatementSyntax FixDeclarationExpressionsAndDeclarationPatterns(StatementSyntax statement,
                                                                                    HashSet <SyntaxAnnotation> variablesToRemove)
            {
                var replacements = new Dictionary <SyntaxNode, SyntaxNode>();

                var declarations = statement.DescendantNodes()
                                   .Where(n => n.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.DeclarationPattern));

                foreach (var node in declarations)
                {
                    switch (node.Kind())
                    {
                    case SyntaxKind.DeclarationExpression:

                        var declaration = (DeclarationExpressionSyntax)node;
                        if (declaration.Designation.Kind() != SyntaxKind.SingleVariableDesignation)
                        {
                            break;
                        }

                        var designation = (SingleVariableDesignationSyntax)declaration.Designation;
                        var name        = designation.Identifier.ValueText;
                        if (variablesToRemove.HasSyntaxAnnotation(designation))
                        {
                            var newLeadingTrivia = new SyntaxTriviaList();
                            newLeadingTrivia = newLeadingTrivia.AddRange(declaration.Type.GetLeadingTrivia());
                            newLeadingTrivia = newLeadingTrivia.AddRange(declaration.Type.GetTrailingTrivia());
                            newLeadingTrivia = newLeadingTrivia.AddRange(designation.GetLeadingTrivia());

                            replacements.Add(declaration, SyntaxFactory.IdentifierName(designation.Identifier)
                                             .WithLeadingTrivia(newLeadingTrivia));
                        }

                        break;

                    case SyntaxKind.DeclarationPattern:
                        var pattern = (DeclarationPatternSyntax)node;
                        if (!variablesToRemove.HasSyntaxAnnotation(pattern))
                        {
                            break;
                        }

                        // We don't have a good refactoring for this, so we just annotate the conflict
                        // For instance, when a local declared by a pattern declaration (`3 is int i`) is
                        // used outside the block we're trying to extract.
                        var identifier    = pattern.Identifier;
                        var annotation    = ConflictAnnotation.Create(CSharpFeaturesResources.Conflict_s_detected);
                        var newIdentifier = identifier.WithAdditionalAnnotations(annotation);
                        replacements.Add(pattern, pattern.WithIdentifier(newIdentifier));

                        break;
                    }
                }

                return(statement.ReplaceNodes(replacements.Keys, (orig, partiallyReplaced) => replacements[orig]));
            }
Example #5
0
        private static async Task ReplaceReferencesAsync(
            Document originalDocument,
            IEnumerable <ValueTuple <IPropertySymbol, ReferenceLocation> > references,
            IDictionary <IPropertySymbol, IFieldSymbol> propertyToBackingField,
            SyntaxNode root, SyntaxEditor editor,
            IReplacePropertyWithMethodsService service,
            string desiredGetMethodName, string desiredSetMethodName,
            CancellationToken cancellationToken)
        {
            if (references != null)
            {
                foreach (var tuple in references)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var property          = tuple.Item1;
                    var referenceLocation = tuple.Item2;
                    var location          = referenceLocation.Location;
                    var nameToken         = root.FindToken(location.SourceSpan.Start);

                    if (referenceLocation.IsImplicit)
                    {
                        // Warn the user that we can't properly replace this property with a method.
                        editor.ReplaceNode(nameToken.Parent, nameToken.Parent.WithAdditionalAnnotations(
                                               ConflictAnnotation.Create(FeaturesResources.Property_referenced_implicitly)));
                    }
                    else
                    {
                        var fieldSymbol = propertyToBackingField.GetValueOrDefault(tuple.Item1);
                        await service.ReplaceReferenceAsync(
                            originalDocument, editor, nameToken,
                            property, fieldSymbol,
                            desiredGetMethodName, desiredSetMethodName,
                            cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        }
Example #6
0
        public async Task <Solution> AddParameterAsync(
            Document invocationDocument,
            IMethodSymbol method,
            ITypeSymbol newParamaterType,
            RefKind refKind,
            string parameterName,
            int?newParameterIndex,
            bool fixAllReferences,
            CancellationToken cancellationToken)
        {
            var solution = invocationDocument.Project.Solution;

            var referencedSymbols = fixAllReferences
                ? await FindMethodDeclarationReferences(invocationDocument, method, cancellationToken).ConfigureAwait(false)
                : method.GetAllMethodSymbolsOfPartialParts();

            var anySymbolReferencesNotInSource = referencedSymbols.Any(symbol => !symbol.IsFromSource());
            var locationsInSource = referencedSymbols.Where(symbol => symbol.IsFromSource());

            // Indexing Locations[0] is valid because IMethodSymbols have one location at most
            // and IsFromSource() tests if there is at least one location.
            var locationsByDocument = locationsInSource.ToLookup(declarationLocation
                                                                 => solution.GetDocument(declarationLocation.Locations[0].SourceTree));

            foreach (var documentLookup in locationsByDocument)
            {
                var document    = documentLookup.Key;
                var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();
                var syntaxRoot  = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var editor    = new SyntaxEditor(syntaxRoot, solution.Workspace);
                var generator = editor.Generator;
                foreach (var methodDeclaration in documentLookup)
                {
                    var methodNode         = syntaxRoot.FindNode(methodDeclaration.Locations[0].SourceSpan);
                    var existingParameters = generator.GetParameters(methodNode);
                    var insertionIndex     = newParameterIndex ?? existingParameters.Count;

                    // if the preceding parameter is optional, the new parameter must also be optional
                    // see also BC30202 and CS1737
                    var parameterMustBeOptional = insertionIndex > 0 &&
                                                  syntaxFacts.GetDefaultOfParameter(existingParameters[insertionIndex - 1]) != null;

                    var parameterSymbol = CreateParameterSymbol(
                        methodDeclaration, newParamaterType, refKind, parameterMustBeOptional, parameterName);

                    var argumentInitializer  = parameterMustBeOptional ? generator.DefaultExpression(newParamaterType) : null;
                    var parameterDeclaration = generator.ParameterDeclaration(parameterSymbol, argumentInitializer)
                                               .WithAdditionalAnnotations(Formatter.Annotation);
                    if (anySymbolReferencesNotInSource && methodDeclaration == method)
                    {
                        parameterDeclaration = parameterDeclaration.WithAdditionalAnnotations(
                            ConflictAnnotation.Create(FeaturesResources.Related_method_signatures_found_in_metadata_will_not_be_updated));
                    }


                    if (method.MethodKind == MethodKind.ReducedExtension)
                    {
                        insertionIndex++;
                    }

                    AddParameter(syntaxFacts, editor, methodNode, insertionIndex, parameterDeclaration, cancellationToken);
                }

                var newRoot = editor.GetChangedRoot();
                solution = solution.WithDocumentSyntaxRoot(document.Id, newRoot);
            }

            return(solution);
        }
Example #7
0
 private static SyntaxAnnotation CreateConflictAnnotation()
 {
     return(ConflictAnnotation.Create(CSharpFeaturesResources.Conflict_s_detected));
 }
        private static async Task <Document> DetectSemanticConflicts(
            Document inlinedDocument,
            SemanticModel newSemanticModelForInlinedDocument,
            SemanticModel semanticModelBeforeInline,
            SymbolInfo originalInitializerSymbolInfo,
            CancellationToken cancellationToken)
        {
            // In this method we detect if inlining the expression introduced the following semantic change:
            // The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.

            // If any semantic changes were introduced by inlining, we update the document with conflict annotations.
            // Otherwise we return the given inlined document without any changes.

            var syntaxRootBeforeInline = await semanticModelBeforeInline.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            // Get all the identifier nodes which were replaced with inlined expression.
            var originalIdentifierNodes = FindReferenceAnnotatedNodes(syntaxRootBeforeInline);

            if (originalIdentifierNodes.IsEmpty())
            {
                // No conflicts
                return(inlinedDocument);
            }

            // Get all the inlined expression nodes.
            var syntaxRootAfterInline = await inlinedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var inlinedExprNodes = syntaxRootAfterInline.GetAnnotatedNodesAndTokens(ExpressionToInlineAnnotation);

            Debug.Assert(originalIdentifierNodes.Count() == inlinedExprNodes.Count());

            var originalNodesEnum = originalIdentifierNodes.GetEnumerator();
            var inlinedNodesEnum  = inlinedExprNodes.GetEnumerator();
            Dictionary <SyntaxNode, SyntaxNode> replacementNodesWithChangedSemantics = null;

            while (originalNodesEnum.MoveNext())
            {
                inlinedNodesEnum.MoveNext();
                var originalNode = originalNodesEnum.Current;

                // expressionToInline is Parenthesized prior to replacement, so get the parenting parenthesized expression.
                var inlinedNode = (ExpressionSyntax)inlinedNodesEnum.Current.AsNode().Parent;
                Debug.Assert(inlinedNode.IsKind(SyntaxKind.ParenthesizedExpression));

                // inlinedNode is the expanded form of the actual initializer expression in the original document.
                // We have annotated the inner initializer with a special syntax annotation "InitializerAnnotation".
                // Get this annotated node and compute the symbol info for this node in the inlined document.
                var innerInitializerInInlineNode = (ExpressionSyntax)inlinedNode.GetAnnotatedNodesAndTokens(InitializerAnnotation).First().AsNode();
                var newInializerSymbolInfo       = newSemanticModelForInlinedDocument.GetSymbolInfo(innerInitializerInInlineNode, cancellationToken);

                // Verification: The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.
                if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
                {
                    newInializerSymbolInfo = newSemanticModelForInlinedDocument.GetSymbolInfo(inlinedNode, cancellationToken);
                    if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
                    {
                        if (replacementNodesWithChangedSemantics == null)
                        {
                            replacementNodesWithChangedSemantics = new Dictionary <SyntaxNode, SyntaxNode>();
                        }

                        replacementNodesWithChangedSemantics.Add(inlinedNode, originalNode);
                    }
                }
            }

            if (replacementNodesWithChangedSemantics == null)
            {
                // No conflicts.
                return(inlinedDocument);
            }

            // Replace the conflicting inlined nodes with the original nodes annotated with conflict annotation.
            Func <SyntaxNode, SyntaxNode, SyntaxNode> conflictAnnotationAdder =
                (SyntaxNode oldNode, SyntaxNode newNode) =>
                newNode.WithAdditionalAnnotations(ConflictAnnotation.Create(CSharpFeaturesResources.ConflictsDetected));

            return(await inlinedDocument.ReplaceNodesAsync(replacementNodesWithChangedSemantics.Keys, conflictAnnotationAdder, cancellationToken).ConfigureAwait(false));
        }
 // Replace the conflicting inlined nodes with the original nodes annotated with conflict annotation.
 static SyntaxNode conflictAnnotationAdder(SyntaxNode oldNode, SyntaxNode newNode) =>
 newNode.WithAdditionalAnnotations(ConflictAnnotation.Create(CSharpFeaturesResources.Conflict_s_detected));
Example #10
0
 private static SyntaxAnnotation CreateConflictAnnotation()
 {
     return(ConflictAnnotation.Create(GettextCatalog.GetString("Conflict(s) detected.")));
 }