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); }
public PositionalArgumentIdentifier(string identifierName, TArgumentSyntax argumentSyntax) : base(identifierName, argumentSyntax) { }
public NamedArgumentIdentifier(string identifierName, TArgumentSyntax argumentSyntax, string declaredName) : base(identifierName, argumentSyntax) { DeclaredName = declaredName; }
protected abstract SyntaxNode GetArgumentExpression(TArgumentSyntax argument);
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); }
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); }
protected abstract TArgumentSyntax GenerateNewArgument( TArgumentSyntax oldArgument, ITypeSymbol conversionType );
protected abstract TExpressionSyntax GetExpressionOfArgument(TArgumentSyntax argument);
/// <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; } }