Exemple #1
0
        private async Task <Solution> FixAsync(
            Document invocationDocument,
            IMethodSymbol method,
            TArgumentSyntax argument,
            SeparatedSyntaxList <TArgumentSyntax> argumentList,
            bool fixAllReferences,
            CancellationToken cancellationToken)
        {
            var solution = invocationDocument.Project.Solution;

            var(argumentType, refKind) = await GetArgumentTypeAndRefKindAsync(invocationDocument, argument, cancellationToken).ConfigureAwait(false);

            // The argumentNameSuggestion is the base for the parameter name.
            // For each method declaration the name is made unique to avoid name collisions.
            var(argumentNameSuggestion, isNamedArgument) = await GetNameSuggestionForArgumentAsync(
                invocationDocument, argument, cancellationToken).ConfigureAwait(false);

            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     = isNamedArgument
                        ? existingParameters.Count
                        : argumentList.IndexOf(argument);

                    // 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, argumentType, refKind, parameterMustBeOptional, argumentNameSuggestion);

                    var argumentInitializer  = parameterMustBeOptional ? generator.DefaultExpression(argumentType) : 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, argument,
                        insertionIndex, parameterDeclaration, cancellationToken);
                }

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

            return(solution);
        }
Exemple #2
0
 public PositionalArgumentIdentifier(string identifierName, TArgumentSyntax argumentSyntax)
     : base(identifierName, argumentSyntax)
 {
 }
Exemple #3
0
 public NamedArgumentIdentifier(string identifierName, TArgumentSyntax argumentSyntax, string declaredName)
     : base(identifierName, argumentSyntax)
 {
     DeclaredName = declaredName;
 }
 protected abstract SyntaxNode GetArgumentExpression(TArgumentSyntax argument);
Exemple #5
0
 protected ArgumentIdentifier(string identifierName, TArgumentSyntax argumentSyntax)
 {
     IdentifierName = identifierName;
     ArgumentSyntax = argumentSyntax;
 }
        private async Task HandleInvocationExpressionAsync(
            CodeFixContext context, TInvocationExpressionSyntax invocationExpression, TArgumentSyntax argumentOpt)
        {
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;
            var semanticModel     = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();

            var expression = syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression);
            var candidates = semanticModel.GetMemberGroup(expression, cancellationToken).OfType <IMethodSymbol>().ToImmutableArray();

            var arguments = (SeparatedSyntaxList <TArgumentSyntax>)syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
            var argumentInsertPositionInMethodCandidates = GetArgumentInsertPositionForMethodCandidates(
                argumentOpt, semanticModel, syntaxFacts, arguments, candidates);

            RegisterFixForMethodOverloads(context, arguments, argumentInsertPositionInMethodCandidates);
        }
Exemple #7
0
        private static async Task <(ITypeSymbol, RefKind)> GetArgumentTypeAndRefKindAsync(Document invocationDocument, TArgumentSyntax argument, CancellationToken cancellationToken)
        {
            var syntaxFacts   = invocationDocument.GetLanguageService <ISyntaxFactsService>();
            var semanticModel = await invocationDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var argumentExpression = syntaxFacts.GetExpressionOfArgument(argument);
            var argumentType       = semanticModel.GetTypeInfo(argumentExpression).Type ?? semanticModel.Compilation.ObjectType;
            var refKind            = syntaxFacts.GetRefKindOfArgument(argument);

            return(argumentType, refKind);
        }
        private async Task HandleObjectCreationExpressionAsync(
            CodeFixContext context,
            TObjectCreationExpressionSyntax objectCreation,
            TArgumentSyntax argumentOpt)
        {
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;
            var semanticModel     = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();

            // Not supported if this is "new { ... }" (as there are no parameters at all.
            var typeNode = syntaxFacts.GetObjectCreationType(objectCreation);

            if (typeNode == null)
            {
                return;
            }

            // If we can't figure out the type being created, or the type isn't in source,
            // then there's nothing we can do.
            var type = semanticModel.GetSymbolInfo(typeNode, cancellationToken).GetAnySymbol() as INamedTypeSymbol;

            if (type == null)
            {
                return;
            }

            if (!type.IsNonImplicitAndFromSource())
            {
                return;
            }

            var arguments = (SeparatedSyntaxList <TArgumentSyntax>)syntaxFacts.GetArgumentsOfObjectCreationExpression(objectCreation);

            var comparer = syntaxFacts.StringComparer;
            var constructorsAndArgumentToAdd = ArrayBuilder <(IMethodSymbol constructor, TArgumentSyntax argument, int index)> .GetInstance();

            foreach (var constructor in type.InstanceConstructors.OrderBy(m => m.Parameters.Length))
            {
                if (constructor.IsNonImplicitAndFromSource() &&
                    NonParamsParameterCount(constructor) < arguments.Count)
                {
                    var argumentToAdd = DetermineFirstArgumentToAdd(
                        semanticModel, syntaxFacts, comparer, constructor,
                        arguments, argumentOpt);

                    if (argumentToAdd != null)
                    {
                        if (argumentOpt != null && argumentToAdd != argumentOpt)
                        {
                            // We were trying to fix a specific argument, but the argument we want
                            // to fix is something different.  That means there was an error earlier
                            // than this argument.  Which means we're looking at a non-viable
                            // constructor.  Skip this one.
                            continue;
                        }

                        constructorsAndArgumentToAdd.Add(
                            (constructor, argumentToAdd, arguments.IndexOf(argumentToAdd)));
                    }
                }
            }

            // Order by the furthest argument index to the nearest argument index.  The ones with
            // larger argument indexes mean that we matched more earlier arguments (and thus are
            // likely to be the correct match).
            foreach (var tuple in constructorsAndArgumentToAdd.OrderByDescending(t => t.index))
            {
                var constructor   = tuple.constructor;
                var argumentToAdd = tuple.argument;

                var parameters = constructor.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
                var signature  = $"{type.Name}({string.Join(", ", parameters)})";

                var title = string.Format(FeaturesResources.Add_parameter_to_0, signature);

                context.RegisterCodeFix(
                    new MyCodeAction(title, c => FixAsync(document, constructor, argumentToAdd, arguments, c)),
                    context.Diagnostics);
            }
        }
 private Task HandleInvocationExpressionAsync(
     CodeFixContext context, TInvocationExpressionSyntax invocationExpression, TArgumentSyntax argumentOpt)
 {
     // Currently we only support this for 'new obj' calls.
     return(SpecializedTasks.EmptyTask);
 }
Exemple #10
0
 protected abstract TArgumentSyntax GenerateNewArgument(
     TArgumentSyntax oldArgument,
     ITypeSymbol conversionType
     );
Exemple #11
0
 protected abstract TExpressionSyntax GetExpressionOfArgument(TArgumentSyntax argument);
Exemple #12
0
            /// <summary>
            /// Test if all argument types can be converted to corresponding parameter types.
            /// </summary>
            /// For example:
            /// class Base { }
            /// class Derived1 : Base { }
            /// class Derived2 : Base { }
            /// class Derived3 : Base { }
            /// void DoSomething(int i, Derived1 d) { }
            /// void DoSomething(string s, Derived2 d) { }
            /// void DoSomething(int i, Derived3 d) { }
            ///
            /// Base b;
            /// DoSomething(1, [||]b);
            ///
            /// *void DoSomething(string s, Derived2 d) { }* is not the perfect match candidate function for
            /// *DoSomething(1, [||]b)* because int and string are not ancestor-descendant relationship. Thus,
            /// Derived2 is not a potential conversion type.
            ///
            /// <param name="argumentList"> The argument list of invocation expression</param>
            /// <param name="parameters"> The parameters of function</param>
            /// <param name="targetArgument">The argument need to be cast.</param>
            /// <param name="targetArgumentConversionType"> Output the corresponding parameter type of
            /// "targetArgument" if function returns true</param>
            /// <returns>
            /// True, if arguments and parameters match perfectly.
            /// "targetArgumentConversionType" outputs the corresponding parameter type of "targetArgument"
            /// False, otherwise.
            /// </returns>
            public bool CanArgumentTypesBeConvertedToParameterTypes(
                SemanticModel semanticModel,
                SyntaxNode root,
                TArgumentListSyntax argumentList,
                ImmutableArray <IParameterSymbol> parameters,
                TArgumentSyntax targetArgument,
                CancellationToken cancellationToken,
                [NotNullWhen(true)] out ITypeSymbol?targetArgumentConversionType
                )
            {
                targetArgumentConversionType = null;

                // No conversion happens under this case
                if (parameters.Length == 0)
                {
                    return(false);
                }

                var syntaxFacts = _provider.SyntaxFacts;

                var arguments = GetArgumentsOfArgumentList(argumentList);

                using var _ = ArrayBuilder <TArgumentSyntax> .GetInstance(out var newArguments);

                for (var i = 0; i < arguments.Count; i++)
                {
                    // Parameter index cannot out of its range, #arguments is larger than #parameter only if
                    // the last parameter with keyword params
                    var parameterIndex = Math.Min(i, parameters.Length - 1);

                    // If the argument has a name, get the corresponding parameter index
                    if (
                        syntaxFacts.GetNameForArgument(arguments[i]) is string name &&
                        name != string.Empty &&
                        !FindCorrespondingParameterByName(name, parameters, ref parameterIndex)
                        )
                    {
                        return(false);
                    }

                    // The argument is either in order with parameters, or have a matched name with parameters.
                    var argumentExpression = GetExpressionOfArgument(arguments[i]);
                    if (argumentExpression == null)
                    {
                        // argumentExpression is null when it is an omitted argument in VB .NET
                        newArguments.Add(arguments[i]);
                        continue;
                    }

                    var parameterType = parameters[parameterIndex].Type;
                    if (
                        parameters[parameterIndex].IsParams &&
                        parameterType is IArrayTypeSymbol paramsType &&
                        _provider.ClassifyConversion(
                            semanticModel,
                            argumentExpression,
                            paramsType.ElementType
                            ).Exists
                        )
                    {
                        newArguments.Add(GenerateNewArgument(arguments[i], paramsType.ElementType));
                        if (arguments[i].Equals(targetArgument))
                        {
                            targetArgumentConversionType = paramsType.ElementType;
                        }
                    }